aaron-official commited on
Commit
df6e544
·
1 Parent(s): 9958640

Major Upgrade: Gradio 6, Global Fintech Assistant, Improved prompts and token limits

Browse files
Files changed (38) hide show
  1. .gitattributes +1 -0
  2. .gitignore +10 -2
  3. .gradio/certificate.pem +31 -0
  4. README.md +45 -52
  5. build_index.py +181 -0
  6. env_example.txt +1 -5
  7. faiss_index/index.faiss +3 -0
  8. faiss_index/index.pkl +3 -0
  9. knowledge_base/mtn_momo/mtn_momo_api_brand_guidelines.md +11 -0
  10. knowledge_base/mtn_momo/mtn_momo_api_callback_docs.md +91 -0
  11. knowledge_base/mtn_momo/mtn_momo_api_error_codes.md +31 -0
  12. knowledge_base/mtn_momo/mtn_momo_api_faq.md +130 -0
  13. knowledge_base/mtn_momo/mtn_momo_api_getting_started.md +37 -0
  14. knowledge_base/mtn_momo/mtn_momo_api_intro.md +14 -0
  15. knowledge_base/mtn_momo/mtn_momo_api_sandbox_use_cases.md +155 -0
  16. knowledge_base/mtn_momo/mtn_momo_api_use_cases.md +161 -0
  17. knowledge_base/mtn_momo/mtn_momo_api_user_and_key_management.md +162 -0
  18. knowledge_base/mtn_momo/mtn_momo_error_codes.md +72 -0
  19. knowledge_base/mtn_momo/mtn_momo_open_apis_ sandbox_documentation.md +1358 -0
  20. knowledge_base/pesapal/pesapal_e-commerce-api-3.0/pesapal_e-commerce_api_3.0_auth_docs.md +67 -0
  21. knowledge_base/pesapal/pesapal_e-commerce-api-3.0/pesapal_e-commerce_api_3.0_e-commerce_intro.md +30 -0
  22. knowledge_base/pesapal/pesapal_e-commerce-api-3.0/pesapal_e-commerce_api_3.0_get_ipn_docs.md +49 -0
  23. knowledge_base/pesapal/pesapal_e-commerce-api-3.0/pesapal_e-commerce_api_3.0_get_transaction_status_docs.md +97 -0
  24. knowledge_base/pesapal/pesapal_e-commerce-api-3.0/pesapal_e-commerce_api_3.0_ipn_docs.md +88 -0
  25. knowledge_base/pesapal/pesapal_e-commerce-api-3.0/pesapal_e-commerce_api_3.0_order_cancellation_api_docs.md +68 -0
  26. knowledge_base/pesapal/pesapal_e-commerce-api-3.0/pesapal_e-commerce_api_3.0_recurring_payments_docs.md +217 -0
  27. knowledge_base/pesapal/pesapal_e-commerce-api-3.0/pesapal_e-commerce_api_3.0_refund_api_docs.md +78 -0
  28. knowledge_base/pesapal/pesapal_e-commerce-api-3.0/pesapal_e-commerce_api_3.0_submit_order_docs.md +165 -0
  29. knowledge_base/pesapal/pesapal_integration_guide.md +40 -0
  30. knowledge_base/pesapal/pesapal_plugins/pesapal_plugin_installation_guides.md +176 -0
  31. knowledge_base/pesapal/pesapal_pos_api/pesapal_pos_api_intro.md +22 -0
  32. knowledge_base/pesapal/pesapal_pos_api/pesapal_wired_pos_api.md +15 -0
  33. knowledge_base/pesapal/pesapal_pos_api/pesapal_wireless_pos_api.md +52 -0
  34. knowledge_base/sentezo/sentezo_wallet_api_docs.md +639 -0
  35. ocs4dev.py +744 -808
  36. pyproject.toml +17 -0
  37. requirements.txt +22 -22
  38. uv.lock +0 -0
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ *.faiss filter=lfs diff=lfs merge=lfs -text
.gitignore CHANGED
@@ -38,5 +38,13 @@ venv.bak/
38
  # Logs
39
  *.log
40
 
41
- # Model cache
42
- .cache/
 
 
 
 
 
 
 
 
 
38
  # Logs
39
  *.log
40
 
41
+ # Model cache (large downloaded model weights — don't commit)
42
+ .cache/
43
+
44
+ # HuggingFace model downloads
45
+ *.bin
46
+ *.safetensors
47
+ *.gguf
48
+
49
+ # FAISS index MUST be committed — DO NOT add faiss_index/ here
50
+ # Run build_index.py once, then: git add faiss_index/
.gradio/certificate.pem ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
3
+ TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
4
+ cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
5
+ WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
6
+ ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
7
+ MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
8
+ h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
9
+ 0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
10
+ A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
11
+ T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
12
+ B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
13
+ B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
14
+ KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
15
+ OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
16
+ jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
17
+ qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
18
+ rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
19
+ HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
20
+ hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
21
+ ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
22
+ 3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
23
+ NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
24
+ ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
25
+ TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
26
+ jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
27
+ oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
28
+ 4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
29
+ mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
30
+ emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
31
+ -----END CERTIFICATE-----
README.md CHANGED
@@ -9,79 +9,72 @@ app_file: app.py
9
  pinned: false
10
  ---
11
 
12
- # 🏦 ocs4dev - Fintech API Integration Assistant
13
 
14
- ## Overview
15
- ocs4dev is a specialized AI assistant designed to help developers integrate fintech APIs including MTN MoMo, Pesapal, Airtel,Sentezo etc. It provides code examples, implementation guidance, and best practices for payment gateway integration.
16
 
17
  ## Features
18
- - 🔧 **Code-Focused**: Optimized for API integration tasks with practical examples
19
- - 🤖 **Multi-Model Support**: Choose between local Qwen2.5-Coder or API models (OpenAI, Anthropic, Google)
20
- - 🔒 **Secure**: No API keys stored permanently
21
- - 📋 **Copy-Friendly**: Built-in code copying functionality
22
- - 🚀 **Fast**: Multiple model options for optimal performance
23
- - 🔄 **RAG-Powered**: Retrieves relevant documentation from vector database
24
 
25
  ## Supported APIs
26
- - **MTN MoMo**: Mobile money integration
27
- - **Airtel API**: Airtel api integration
28
- - **Pesapal**: Payment gateway services
29
- - **Sentezo**: Mobile payment platform
30
- - And more fintech APIs...
31
 
32
- ## Model Options
 
 
 
 
33
 
34
- ### Local Model (Free)
35
- - **Qwen2.5-Coder-7B-Instruct-AWQ**: Quantized model optimized for code generation
36
- - Requires GPU for best performance
37
- - No API key needed
38
 
39
- ### API Models
40
- - **OpenAI**: GPT-4o-mini (budget) / O4-mini (premium)
41
- - **Anthropic**: Claude-3.5-Sonnet / Claude-4-Sonnet
42
- - **Google**: Gemini-2.0-Flash / Gemini-2.0-Flash-Thinking
 
 
43
 
44
  ## Quick Start
 
45
  1. Visit the [Space URL](https://huggingface.co/spaces/YOUR_USERNAME/ocs4dev)
46
- 2. Choose your preferred model provider
47
- 3. Add API keys if using cloud models
48
- 4. Start asking questions about fintech API integration!
49
 
50
- ## Example Questions
51
- - "How do I authenticate with MTN MoMo API?"
52
- - "Show me a Pesapal payment integration example"
53
- - "What are the required headers for Sentezo API?"
54
- - "How do I handle payment webhooks?"
55
- - "Best practices for API error handling"
56
 
57
- ## Environment Variables
58
- For self-hosting, set these environment variables:
59
  ```bash
60
- # Supabase (for vector store)
61
- SUPABASE_URL=your-supabase-url
62
- SUPABASE_SERVICE_KEY=your-service-key
 
 
 
 
 
 
 
 
 
 
63
 
64
- # API Keys (optional)
65
  OPENAI_API_KEY=your-openai-key
66
  ANTHROPIC_API_KEY=your-anthropic-key
67
  GOOGLE_API_KEY=your-google-key
68
  ```
69
 
70
  ## Technical Stack
71
- - **Frontend**: Gradio
72
- - **LLM Framework**: LangChain
73
- - **Vector Store**: Supabase
74
- - **Models**: Qwen2.5-Coder, OpenAI, Anthropic, Google
75
- - **Embeddings**: OpenAI text-embedding-3-small
76
 
77
- ## Contributing
78
- Contributions are welcome! Please feel free to submit a Pull Request.
79
-
80
- ## License
81
- This project is licensed under the MIT License.
82
-
83
- ## Support
84
- For issues or questions, please open an issue on GitHub or contact me.
85
 
86
  ---
87
- Built with ❤️ using Qwen2.5-Coder, LangChain, and Gradio
 
9
  pinned: false
10
  ---
11
 
12
+ # 🏦 ocs4dev Fintech API Integration Assistant
13
 
14
+ Specialized AI chatbot for integrating African fintech APIs. Powered by **local FAISS RAG** — no cloud database required.
 
15
 
16
  ## Features
17
+
18
+ - 🔍 **Local RAG** FAISS vector search over bundled API docs (no Supabase, no cloud DB)
19
+ - 🤖 **Multi-Model** Google Gemini 3, OpenAI GPT-5, Anthropic Claude 4, or local Qwen 3B
20
+ - 📋 **Code-Focused** Working code examples with proper error handling
21
+ - 🔒 **Secure** API keys entered at runtime, never stored
22
+ - 🚀 **HF Spaces Ready** CPU-optimized, fits free tier
23
 
24
  ## Supported APIs
 
 
 
 
 
25
 
26
+ | API | Coverage |
27
+ |---|---|
28
+ | **MTN MoMo** | Collections, Disbursements, Remittances, Callbacks |
29
+ | **Pesapal** | E-Commerce API 3.0, POS, Recurring Payments, IPN |
30
+ | **Sentezo** | Wallet Deposits, Withdrawals, Bank Transfers |
31
 
32
+ ## Model Options (March 2026)
 
 
 
33
 
34
+ | Provider | Budget | Premium |
35
+ |---|---|---|
36
+ | **Google** ⭐ | Gemini 3 Flash | Gemini 3.1 Pro |
37
+ | **OpenAI** | GPT-5 mini | GPT-5.2 |
38
+ | **Anthropic** | Claude Haiku 4.5 | Claude Opus 4.6 |
39
+ | **Local** | Qwen2.5-Coder-3B-Int4 | — |
40
 
41
  ## Quick Start
42
+
43
  1. Visit the [Space URL](https://huggingface.co/spaces/YOUR_USERNAME/ocs4dev)
44
+ 2. Choose a model in the **☰ Settings** panel
45
+ 3. Add your API key (or use the free local model)
46
+ 4. Start asking questions!
47
 
48
+ ## Self-Hosting / Development
 
 
 
 
 
49
 
 
 
50
  ```bash
51
+ # Install dependencies
52
+ pip install -r requirements.txt
53
+
54
+ # Build the FAISS vector index (run once)
55
+ python build_index.py
56
+
57
+ # Start the app
58
+ python app.py
59
+ ```
60
+
61
+ ## Environment Variables
62
+
63
+ Only needed if you want API keys pre-loaded (users can also enter them in the UI):
64
 
65
+ ```bash
66
  OPENAI_API_KEY=your-openai-key
67
  ANTHROPIC_API_KEY=your-anthropic-key
68
  GOOGLE_API_KEY=your-google-key
69
  ```
70
 
71
  ## Technical Stack
 
 
 
 
 
72
 
73
+ - **Frontend**: Gradio 5
74
+ - **LLM Framework**: LangChain
75
+ - **Vector Store**: FAISS (local, no cloud)
76
+ - **Embeddings**: sentence-transformers/all-MiniLM-L6-v2 (free, CPU)
77
+ - **Local Model**: Qwen2.5-Coder-3B-Instruct-Int4
 
 
 
78
 
79
  ---
80
+ Built with ❤️ by Aaron
build_index.py ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ build_index.py — One-time FAISS index builder for ocs4dev
3
+ ==========================================================
4
+ Run this script ONCE locally before deploying to HuggingFace Spaces.
5
+ It reads all markdown files from knowledge_base/ and builds a FAISS
6
+ vector index that the main app uses for retrieval.
7
+
8
+ After running, commit the generated faiss_index/ folder to your repo:
9
+ git add faiss_index/
10
+ git commit -m "Add FAISS vector index"
11
+ git push
12
+
13
+ Usage:
14
+ python build_index.py
15
+ python build_index.py --kb ./knowledge_base --out ./faiss_index
16
+ """
17
+
18
+ import os
19
+ import argparse
20
+ from pathlib import Path
21
+
22
+ from langchain_community.document_loaders import TextLoader, DirectoryLoader
23
+ from langchain_text_splitters import RecursiveCharacterTextSplitter
24
+ from langchain_huggingface import HuggingFaceEmbeddings
25
+ from langchain_community.vectorstores import FAISS
26
+
27
+ # --- Defaults ---
28
+ DEFAULT_KB_DIR = "./knowledge_base"
29
+ DEFAULT_OUT_DIR = "./faiss_index"
30
+ EMBEDDING_MODEL = "sentence-transformers/all-MiniLM-L6-v2"
31
+ CHUNK_SIZE = 1000
32
+ CHUNK_OVERLAP = 200
33
+
34
+
35
+ def load_documents(kb_dir: str):
36
+ """Recursively load all .md files from the knowledge base directory."""
37
+ print(f"\n📂 Loading documents from: {kb_dir}")
38
+ loader = DirectoryLoader(
39
+ kb_dir,
40
+ glob="**/*.md",
41
+ loader_cls=TextLoader,
42
+ loader_kwargs={"encoding": "utf-8"},
43
+ show_progress=True,
44
+ use_multithreading=True,
45
+ )
46
+ docs = loader.load()
47
+
48
+ # Enrich metadata with API provider based on folder name
49
+ for doc in docs:
50
+ source = doc.metadata.get("source", "")
51
+ path_parts = Path(source).parts
52
+ # Detect provider from folder structure
53
+ for part in path_parts:
54
+ if "mtn" in part.lower():
55
+ doc.metadata["provider"] = "MTN MoMo"
56
+ break
57
+ elif "pesapal" in part.lower():
58
+ doc.metadata["provider"] = "Pesapal"
59
+ break
60
+ elif "sentezo" in part.lower() or "ssentezo" in part.lower():
61
+ doc.metadata["provider"] = "Sentezo"
62
+ break
63
+ else:
64
+ doc.metadata["provider"] = "General"
65
+
66
+ print(f"✅ Loaded {len(docs)} documents")
67
+ return docs
68
+
69
+
70
+ def split_documents(docs):
71
+ """Split documents into overlapping chunks for better retrieval."""
72
+ print(f"\n✂️ Splitting into chunks (size={CHUNK_SIZE}, overlap={CHUNK_OVERLAP})...")
73
+ splitter = RecursiveCharacterTextSplitter(
74
+ chunk_size=CHUNK_SIZE,
75
+ chunk_overlap=CHUNK_OVERLAP,
76
+ separators=["\n## ", "\n### ", "\n#### ", "\n\n", "\n", ". ", " "],
77
+ )
78
+ chunks = splitter.split_documents(docs)
79
+ print(f"✅ Created {len(chunks)} chunks from {len(docs)} documents")
80
+ return chunks
81
+
82
+
83
+ def build_faiss_index(chunks, out_dir: str):
84
+ """Generate embeddings and save FAISS index to disk."""
85
+ print(f"\n🔢 Loading embedding model: {EMBEDDING_MODEL}")
86
+ print(" (This may take a minute on first run — model will be cached)")
87
+ embeddings = HuggingFaceEmbeddings(
88
+ model_name=EMBEDDING_MODEL,
89
+ model_kwargs={"device": "cpu"},
90
+ encode_kwargs={"normalize_embeddings": True},
91
+ )
92
+
93
+ print(f"\n🔄 Building FAISS index from {len(chunks)} chunks...")
94
+ vector_store = FAISS.from_documents(chunks, embeddings)
95
+
96
+ os.makedirs(out_dir, exist_ok=True)
97
+ vector_store.save_local(out_dir)
98
+ print(f"✅ FAISS index saved to: {out_dir}/")
99
+ return vector_store
100
+
101
+
102
+ def run_verification(vector_store):
103
+ """Run a quick search test to confirm the index works."""
104
+ print("\n🧪 Running verification queries...")
105
+ test_queries = [
106
+ ("MTN MoMo authentication", "MTN MoMo"),
107
+ ("Pesapal payment integration", "Pesapal"),
108
+ ("Sentezo wallet deposit", "Sentezo"),
109
+ ]
110
+
111
+ all_passed = True
112
+ for query, expected_provider in test_queries:
113
+ try:
114
+ results = vector_store.similarity_search(query, k=3)
115
+ providers = [r.metadata.get("provider", "?") for r in results]
116
+ hit = any(expected_provider in p for p in providers)
117
+ status = "✅" if hit else "⚠️ "
118
+ if not hit:
119
+ all_passed = False
120
+ print(f" {status} '{query}'")
121
+ print(f" Top result: {Path(results[0].metadata.get('source', '?')).name}")
122
+ print(f" Preview: {results[0].page_content[:80].strip()}...")
123
+ except Exception as e:
124
+ print(f" ❌ '{query}' — Error: {e}")
125
+ all_passed = False
126
+
127
+ return all_passed
128
+
129
+
130
+ def main():
131
+ parser = argparse.ArgumentParser(
132
+ description="Build FAISS vector index for ocs4dev"
133
+ )
134
+ parser.add_argument(
135
+ "--kb", default=DEFAULT_KB_DIR,
136
+ help=f"Path to knowledge base directory (default: {DEFAULT_KB_DIR})"
137
+ )
138
+ parser.add_argument(
139
+ "--out", default=DEFAULT_OUT_DIR,
140
+ help=f"Output directory for FAISS index (default: {DEFAULT_OUT_DIR})"
141
+ )
142
+ args = parser.parse_args()
143
+
144
+ print("=" * 60)
145
+ print(" ocs4dev — FAISS Vector Index Builder")
146
+ print("=" * 60)
147
+
148
+ # Validate knowledge base dir
149
+ if not os.path.exists(args.kb):
150
+ print(f"\n❌ Knowledge base directory not found: {args.kb}")
151
+ print(" Make sure your knowledge_base/ folder exists with .md files.")
152
+ return
153
+
154
+ # Load → split → index
155
+ docs = load_documents(args.kb)
156
+ if not docs:
157
+ print("❌ No .md files found in the knowledge base!")
158
+ return
159
+
160
+ chunks = split_documents(docs)
161
+ vector_store = build_faiss_index(chunks, args.out)
162
+
163
+ # Verify
164
+ passed = run_verification(vector_store)
165
+
166
+ print("\n" + "=" * 60)
167
+ if passed:
168
+ print("🎉 Index built and verified successfully!")
169
+ else:
170
+ print("⚠️ Index built, but some queries returned unexpected results.")
171
+ print(" Consider checking your knowledge base content.")
172
+ print()
173
+ print("📋 Next steps:")
174
+ print(f" 1. git add {args.out}/")
175
+ print(f" 2. git commit -m 'Add FAISS vector index'")
176
+ print(f" 3. git push (to deploy to HuggingFace Spaces)")
177
+ print("=" * 60)
178
+
179
+
180
+ if __name__ == "__main__":
181
+ main()
env_example.txt CHANGED
@@ -1,8 +1,4 @@
1
- # Supabase Configuration (Required for vector store)
2
- SUPABASE_URL=your-supabase-project-url
3
- SUPABASE_SERVICE_KEY=your-supabase-service-key
4
-
5
- # API Keys (Optional - users can add via UI)
6
  OPENAI_API_KEY=your-openai-api-key
7
  ANTHROPIC_API_KEY=your-anthropic-api-key
8
  GOOGLE_API_KEY=your-google-api-key
 
1
+ # API Keys (optional users can also enter via the UI)
 
 
 
 
2
  OPENAI_API_KEY=your-openai-api-key
3
  ANTHROPIC_API_KEY=your-anthropic-api-key
4
  GOOGLE_API_KEY=your-google-api-key
faiss_index/index.faiss ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a1f3acd97960c02d07d137e5c67e9f0159c3e3b4df1c8085b5da4aad3cbd0602
3
+ size 440877
faiss_index/index.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:7bf44f2327bab40775d8c7b0e4005ac4ad377608a1583c92d654ab19687eb236
3
+ size 210119
knowledge_base/mtn_momo/mtn_momo_api_brand_guidelines.md ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # MoMo API
2
+
3
+ ## Brand Guidelines
4
+
5
+ The **MTN MoMo logo** will only be used at the **payment stage** of your customer journey. Below are the brand guidelines for use of the **MTN MoMo Brand**:
6
+
7
+ - When used, the logo sits in the **bottom right hand corner** of the messaging, against a **white** or **MoMo Blue** background
8
+ - The distance between the logo and the border to the right and border to the bottom is always **equal** and equivalent size to the **blue bulb** that forms the MTN logo
9
+ - The size of the logo will occupy the rectangle space of the ad where it features, scaled to feature **prominently** relative to all the other content in the ad
10
+
11
+ Reference to or usage of the **MTN MoMo logo** in other instances other than at the payment stage in the customer journey, will be restricted a statement of endorsement i.e. **"Supported by MTN MoMo"**. The use of the **MTN MoMo** in these instances is **prohibited**.
knowledge_base/mtn_momo/mtn_momo_api_callback_docs.md ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # MoMo API
2
+
3
+ ## Callback
4
+
5
+ ### Setting up a callback URL
6
+
7
+ **Transfer** and **RequestToPay** APIs are **Asynchronous** in MTN MoMo API Platform
8
+
9
+ When a merchant system sends a POST of either `/transfer`, or `/requesttopay` APIs, the Gateway validates the request and then responds with **'202 Accepted'**
10
+
11
+ The transaction is then queued for processing.
12
+
13
+ Once processed, a callback with the final result of the transaction is sent to the merchant system
14
+
15
+ In order to receive the callback for your transactions, please consider the following:
16
+
17
+ #### a) On Sandbox
18
+
19
+ - Register your callback host by specifying the domain as **providerCallbackHost** when creating your API Keys. On production this will be done via the Account Portal
20
+ - Specify the callback URL in each of your `/requesttopay` or `/transfer` POST
21
+ - Use **http** and not **https** on sandbox
22
+ - Allow **PUT & POST** on your callback listener host
23
+
24
+ #### b) On Production
25
+
26
+ - After Go-live you will be provided a link to log on to your **Accounts Portal**
27
+ - You will be required to register you callback host on the portal when creating your API keys as shown below
28
+ - Only **https** is allowed on production
29
+ - Allow **PUT & POST** on your callback listener host
30
+
31
+ The Wallet Platform will only send the callback **once**. There is no retry on the callback if the Partner system does not respond. A merchant system can, in cases where a callback was not received, poll for the transaction status as described in the **GET method**
32
+
33
+ Let's look at the **Deposit API** under the product set **Disbursement** for instance.
34
+
35
+ The are two Deposit APIs - **Deposit-V1** and **Deposit-V2**.
36
+
37
+ The callback request for **Deposit-V1** can be sent via
38
+
39
+ ```
40
+ https://momodeveloper.mtn.com/API-collections#api=disbursement&operation=Deposit-V1
41
+ ```
42
+
43
+ The callback received would be of the type **POST**.
44
+
45
+ ### Approved Intermediate CA's for Open API
46
+
47
+ For Open API callbacks to function, the **3PP Intermediate certificate chains** must be imported on the PG's **tls_keystore** and callback URL's are required to use **https L7 protocol**:
48
+
49
+ **CN** – Refers to the Common name of the immediate intermediate CA Chain
50
+
51
+ **Alias** – Name that is used while storing the Certificate in PG's tls_keystore
52
+
53
+ Below is the list of **Approved Intermediate CA's** that's already available to use:
54
+
55
+ | Alias | CN |
56
+ |-------|-----|
57
+ | `GTS_CA_1C3` | CN=GTS CA 1C3; O=Google Trust Services LLC; C=US |
58
+ | `Go_Daddy_Secure_Certificate_Authority_-_G2` | CN=Go Daddy Secure Certificate Authority - G2; OU=http://certs.godaddy.com/repository/; O=GoDaddy.com, Inc.; C=US |
59
+ | `R3` | CN=R3; O=Let's Encrypt; C=US |
60
+ | `Sectigo_RSA_Domain_Validation_Secure_Server_CA` | CN=Sectigo RSA Domain Validation Secure Server CA; O=Sectigo Limited; C=GB |
61
+ | `AmazonRCA4` | CN = Amazon Root CA 4,O = Amazon,C = US |
62
+ | `AmazonCA1B` | CN = Amazon,OU = Server CA 1B,O = Amazon,C = US |
63
+ | `Encryption_Everywhere_DV_TLS_CA_-_G1` | CN=Encryption Everywhere DV TLS CA - G1; OU=www.digicert.com; O=DigiCert Inc; C=US |
64
+ | `cPanel,_Inc._Certification_Authority` | CN=cPanel, Inc. Certification Authority; O=cPanel, Inc.; C=US |
65
+ | `DigiCert_SHA2_Secure_Server_CA` | CN=DigiCert SHA2 Secure Server CA; O=DigiCert Inc; C=US |
66
+ | `GTS_CA_1D4` | CN=GTS CA 1D4; O=Google Trust Services LLC; C=US |
67
+ | `Cloudflare_Inc_ECC_CA-3` | CN=Cloudflare Inc ECC CA-3; O=Cloudflare, Inc.; C=US |
68
+ | `DigiCert_SHA2_High_Assurance_Server_CA` | CN=DigiCert SHA2 High Assurance Server CA; OU=www.digicert.com; O=DigiCert Inc; C=US |
69
+ | `ZeroSSL_RSA_Domain_Secure_Site_CA` | CN=ZeroSSL RSA Domain Secure Site CA; O=ZeroSSL; C=AT |
70
+ | `AlphaSSL_CA_-_SHA256_-_G2` | CN=AlphaSSL CA - SHA256 - G2; O=GlobalSign nv-sa; C=BE |
71
+ | `RapidSSL_TLS_DV_RSA_Mixed_SHA256_2020_CA-1` | CN=RapidSSL TLS DV RSA Mixed SHA256 2020 CA-1; O=DigiCert Inc; C=US |
72
+ | `Thawte_RSA_CA_2018` | CN=Thawte RSA CA 2018; OU=www.digicert.com; O=DigiCert Inc; C=US |
73
+ | `GoGetSSL_RSA_DV_CA` | CN=GoGetSSL RSA DV CA; O=GoGetSSL; C=LV |
74
+ | `Gandi_Standard_SSL_CA_2` | CN=Gandi Standard SSL CA 2; O=Gandi; C=FR |
75
+ | `GlobalSign_RSA_OV_SSL_CA_2018` | CN=GlobalSign RSA OV SSL CA 2018; O=GlobalSign nv-sa; C=BE |
76
+ | `DigiCert_TLS_RSA_SHA256_2020_CA1` | CN=DigiCert TLS RSA SHA256 2020 CA1; O=DigiCert Inc; C=US |
77
+ | `GeoTrust_RSA_CA_2018` | CN=GeoTrust RSA CA 2018; OU=www.digicert.com; O=DigiCert Inc; C=US |
78
+ | `Microsoft_RSA_TLS_CA_02` | CN=Microsoft RSA TLS CA 02; O=Microsoft Corporation; C=US |
79
+ | `SSL.com_RSA_SSL_subCA` | CN=SSL.com RSA SSL subCA; O=SSL Corporation; C=US |
80
+ | `RapidSSL_TLS_RSA_CA_G1` | CN=RapidSSL TLS RSA CA G1; OU=www.digicert.com; O=DigiCert Inc; C=US |
81
+ | `Thawte_EV_RSA_CA_2018` | CN=Thawte EV RSA CA 2018; OU=www.digicert.com; O=DigiCert Inc; C=US |
82
+ | `Microsoft_Azure_TLS_Issuing_CA_05` | CN=Microsoft Azure TLS Issuing CA 05; O=Microsoft Corporation; C=US |
83
+ | `GeoTrust_TLS_DV_RSA_Mixed_SHA256_2020_CA-1` | CN=GeoTrust TLS DV RSA Mixed SHA256 2020 CA-1; O=DigiCert Inc; C=US |
84
+ | `E1` | CN=E1; O=Let's Encrypt; C=US |
85
+ | `Sectigo_ECC_Domain_Validation_Secure_Server_CA` | CN=Sectigo ECC Domain Validation Secure Server CA; O=Sectigo Limited; C=GB |
86
+ | `COMODO_RSA_Domain_Validation_Secure_Server_CA` | CN=COMODO RSA Domain Validation Secure Server CA; O=COMODO CA Limited; C=GB |
87
+ | `entrustl1k_.entrustrootca-g2` | CN = Entrust Certification Authority - L1K,OU = (c) 2012 Entrust\, Inc. - for authorized use only,OU = See www.entrust.net/legal-terms,O = Entrust\, Inc.,C = US |
88
+
89
+ **NOTE**: In case any Partner's callback URL is not part of the **Approved Intermediate CA's**, callbacks might not work for the said Partners.
90
+
91
+ ---
knowledge_base/mtn_momo/mtn_momo_api_error_codes.md ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # MoMo API
2
+
3
+ ## Common Error Codes
4
+
5
+ The complete definitions of error codes are found in the swagger documentation. Below is the list of error codes available.
6
+
7
+ ### Common Error Codes
8
+
9
+ | HTTP Code | Error Response Code | Description | Action |
10
+ |-----------|-------------------|-------------|--------|
11
+ | **409** | `RESOURCE_ALREADY_EXIST` | Duplicated Reference ID. Every request must have a unique reference ID; using an ID of the previous request will result in this error response. | Check **X-Reference ID** used is unique and is in **UUID V4** format |
12
+ | **401** | `ACCESS DENIED DUE TO INVALID SUBSCRIPTION KEY` | Authentication failed. Credentials invalid. Header **Ocp-APIM-Subscription-Key** value is incorrect. | Check the User Profile Section to verify the related product subscription key is used. **Collection**, **Disbursement** and **Remittance** have different subscription keys. If the primary key doesn't work, try the secondary key. Contact MTN support if both provided keys aren't working.<br>- Sandbox subscription key are located in https://momodeveloper.mtn.com/developer<br>- Production subscription key are located in https://momoapi.mtn.com/developer |
13
+ | **404** | `RESOURCE NOT FOUND` | Reference ID not found. Requested resource does not exist. Predominantly occurs with **Get Status API** and implies that the requested reference ID does not exist. This results in the Request to Debit or Transfer transaction being unsuccessful. | Check if the original request to pay or the transfer (disbursement) operation was successful with response code **202**. |
14
+ | **400** | `REQUEST REJECTED/ BAD REQUEST` | Bad request. Request does not follow the specification. | This relates to any of the below scenarios:<br>- Incorrect/wrong values in the headers, and/or the **X-ref ID** does not meet **UUID Version 4**.<br>- Inputting a Body in an API that is not supported e.g. `/Token API`<br>- Having unsupported special characters in the Body request for example an apostrophe (`'`).<br>- Invalid currency – needs to match the target environment currency.<br>- More than 160 characters in the note and message; explore utilizing the notification API for increased number of characters.<br>- The URL posted to needs to reviewed e.g. incorrect number of forward slashes (`///`). |
15
+ | **403** | `FORBIDDEN IP` | Authorization failed. IP not authorized to utilize **Disbursement API**. | Share your originating Public IP from which the APIs are called with your MTN Account Manager. |
16
+ | **500** | `NOT_ALLOWED` | Authorization failed. User does not have permission. The account authenticated with the Request via Token is restricted. | Contact your MTN Account Manager. |
17
+ | **500** | `NOT_ALLOWED_TARGET_ENVIRONMENT` | Value passed in header **X-Target-Environment** is incorrect | Use the correct **X-Target-Environment** corresponding to below country:<br>- MTN Uganda = `mtnuganda`<br>- MTN Ghana = `mtnghana`<br>- MTN Ivory Coast = `mtnivorycoast`<br>- MTN Zambia = `mtnzambia`<br>- MTN Cameroon = `mtncameroon`<br>- MTN Benin = `mtnbenin`<br>- MTN Congo = `mtncongo`<br>- MTN Swaziland = `mtnswaziland`<br>- MTN GuineaConakry = `mtnguineaconakry`<br>- MTN SouthAfrica = `mtnsouthafrica`<br>- MTN Liberia = `mtnliberia`<br><br>For Test Environment = `sandbox` |
18
+ | **500** | `INVALID_CALLBACK_URL_HOST` | Callback URL with different host name to configured for API User. Check the Host of the Call Back URL in the request header; this needs to match what was configured on the partner portal when creating the API user and Key. | Host needs to be configured using **Hostname** and not **IP address**. |
19
+ | **500** | `INVALID_CURRENCY` | Currency not supported on the requested account | Use **Currency Code** specific to the Country. |
20
+ | **503** | `SERVICE_UNAVAILABLE` | Service temporary unavailable, try again later | Enquire with MTN Support. |
21
+
22
+ ### Common Error Responses with Action
23
+
24
+ | Type | Description | Action |
25
+ |------|-------------|--------|
26
+ | `INTERNAL_PROCESSING_ERROR` | Default or Generic error code used when there is no specific error mapping. This predominantly occurs due to insufficient customer funds to complete the transaction. Also related to service denied or Wallet Platform is not reachable. | Advice customer to ensure they have sufficient funds to complete the transaction. Also request the customer to retry the transaction. If the problem still occurs with sufficient customer funds, please contact your MTN Account Manager for further investigation. |
27
+ | `PAYEE_NOT_FOUND` | The **MSISDN** being paid to is invalid. | **MSISDN** format must include country code. **MSISDN** is not registered for Mobile Money Service. |
28
+ | `PAYER_NOT_FOUND` | **MSISDN** of the number from whom the money was requested in invalid. | **MSISDN** format must include country code. **MSISDN** is not registered for Mobile Money Service. |
29
+ | `COULD_NOT_PERFORM_TRANSACTION` | This can be attributed to transaction timeout. This predominantly occurs with a delay to approve a transaction within the given time frame (5 minutes). | Advise customer to try again and approve transaction within **5 minutes**. |
30
+
31
+ ---
knowledge_base/mtn_momo/mtn_momo_api_faq.md ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # MoMo API FAQ
2
+
3
+ ## Authentication
4
+
5
+ ### 1. What credentials do I need once I have subscribed to a product?
6
+
7
+ - **Subscription Key** - received upon subscription to a product. It is used for authenticating the number of API calls. For information about subscription keys, refer to point 2
8
+ - **API User and API Key** for bearer Oauth 2.0 token. In the sandbox they are self-generated from the APIs. In production these are generated from the partner portal (part of onboarding)
9
+
10
+ ### 2. What is a subscription Key?
11
+
12
+ - This Key is used to authenticate and limit the number of calls that can be made
13
+ - The subscription key is assigned to the **Ocp-Apim-Subscription-Key** parameter of the header
14
+ - The Subscription Key can be found in your user profile
15
+ - Developers - can use either the Primary Key or Secondary Key for every product they subscribe to allow access to the API
16
+ - A developer cannot access or utilize any of the respective APIs without a Subscription Key
17
+ - Different subscription keys can be used for different product APIs; please check in your user profile
18
+
19
+ ### 3. What is the API User and API Key for Oauth 2.0?
20
+
21
+ - The API User and API Key are used together with your subscription key to grant access to the -wallet system and is applicable to a specific country
22
+ - The API User and API Key are generated using respective APIs in the sandbox
23
+ - API user and Key are wholly managed by the merchant through Partner Portal for PRODUCTION setups
24
+ - Merchants can generate/revoke/refresh API Keys through the Partner Portal
25
+ - For Sandbox API User and Key is generated using an API
26
+
27
+ ### 4. How do I generate an Oauth 2.0 token?
28
+
29
+ - You require an API User and API Key in format **APIUSER: APIKey** `e16510xx-7282-4a39-xx8b-da054889a33a:xx1894d23a8d4xxdadaf62f39dae99xx`
30
+ - Convert the concatenation of APIUser: APIKey into Base64 format
31
+ - Use the Base64 format to generate the authorization token, the result will look similar to this string `ZTE2NTEwY2xtNzI4Mi00YTx5LTg5OGItZGEwNTQ4ODlhMzNhOjg1MTg5NGQyM2E4ZDQxMW RhZGFmNxJmMzlkYWU5OcY4`
32
+
33
+ ### 5. How do I create, provision and manage the API user and API key?
34
+
35
+ - Please review Sandbox provisioning process under API Sandbox
36
+ - The API User and API Key are used to grant access to the wallet system applicable to a specific country
37
+ - API user and Key are wholly managed by the merchant through Partner Portal in production
38
+ - Developers can generate or revoke API Keys through the Partner Portal in production
39
+
40
+ ### 6. How do I generate a UUID for my transactions?
41
+
42
+ - This ID is used, for example, validating the status of the request. 'Universal Unique ID' for the transaction generated using UUID version 4
43
+ - Example of Version 4 UUID: `ca58fd96-2478-4624-b663-bdacd5f914ca`
44
+
45
+ ## Callback
46
+
47
+ ### 7. I am not getting any callbacks. How do I verify or configure my callback URL?
48
+
49
+ Check that you have:
50
+
51
+ - Registered a **ProviderCallbackHost** when creating your API User
52
+ - You have specified a callback URL in the Request
53
+ - The Callback URL MUST belong to the same domain as the ProvideCallbackHost. Subdomains are not allowed.
54
+
55
+ **Example:**
56
+
57
+ If you have specified your ProviderCallbackHost as `mycallback.com` then the callback URL shall be specified as `https://mycallback.com/`
58
+
59
+ Using `https://subdomain.mycallback.com/` as callback URL will not work
60
+
61
+ Callbacks in the sandbox are set by setting the callback host when creating an API user, if my callback URL for traffic is `https://testsite.test.com:1313/api/callback`, the callback host is `testsite.test.com` and the callback host is what is set with the user, but the call back URL is used in every request where a callback is expected
62
+
63
+ In the production environment, the callback is set in the merchant portal and instructions are sent as part of the onboarding documentation
64
+
65
+ ### Setting up a Callback URL
66
+
67
+ Transfer and RequestToPay APIs are Asynchronous in MTN MoMo API Platform
68
+
69
+ When a merchant system sends a POST of either `/transfer`, or `/requesttopay` APIs, the Gateway validates the request and then responds with '202 Accepted'
70
+
71
+ The transaction is then queued for processing.
72
+
73
+ Once processed, a callback with the final result of the transaction is sent to the merchant system
74
+
75
+ In order to receive the callback for your transactions, please consider the following:
76
+
77
+ #### a) On Sandbox
78
+
79
+ - Register your callback host by specifying the domain as providerCallbackHost when creating your API Keys. On production this will be done via the Account Portal
80
+ - Specify the callback URL in each of your `/requesttopay` or `/tranfer` POST
81
+ - Use http and not https on sandbox
82
+ - Allow PUT & POST on your callback listener host
83
+
84
+ #### b) On Production
85
+
86
+ - After Go-live you will be provided a link to log on to your Accounts Portal
87
+ - You will be required to register you callback host on the portal when creating your API keys as shown below
88
+ - Only https is allowed on production
89
+ - Allow PUT & POST on your callback listener host
90
+
91
+ **Callback Create API User**
92
+
93
+ The Wallet Platform will only send the callback once. There is no retry on the callback if the Partner system does not respond. A merchant system can, in cases where a callback was not received , poll for the transaction status as described in the GET method
94
+
95
+ ## Error Code
96
+
97
+ Transfer is used for transferring money from the provider account to a customer.
98
+
99
+ ### 8. What are the common error codes you may expect?
100
+
101
+ - The error codes are categorized as follows (**Common Error Codes**, **Preapproval Error Codes**, **RequestToPay Error Codes**, **Transfer Error Codes** and **Validate Account Holder Error Codes**)
102
+ - The API User and API Key are used to grant access to the wallet system applicable to a specific country
103
+ - More information on error codes can be found under the documentation section
104
+
105
+ ## Support
106
+
107
+ ### 9. How do I get support and join the developer community?
108
+
109
+ - Select the 'contact support' button
110
+ - Select a country from the drop-down list
111
+ - Choose WhatsApp and join the developer group - start chatting
112
+ - Or connect via Skype
113
+
114
+ ## On-Boarding
115
+
116
+ ### 10. What steps do I need to follow go live?
117
+
118
+ - Select the 'Go-Live' option on portal
119
+ - Select Operating country from predefined list to take products Live in that Operating country
120
+ - Choose a Package and Product set
121
+ - Provide KYC Information for business owner and business details
122
+ - Download, complete and submit KYC documents as well as signed contract
123
+ - Submit documents through the Sandbox Portal for Vetting and Approval
124
+ - Access to the partner portal granted for you to create the API User and API Key
125
+
126
+ ## MoMo API Developer Community
127
+
128
+ Join the MoMo API Developer LinkedIn community and be the first to learn about APIs, events, and news relevant to you for accurate and deeper understanding of the APIs.
129
+
130
+ Let's learn, collaborate, and get insights from the developer community to take your business to the next level using MTN MoMo APIs.
knowledge_base/mtn_momo/mtn_momo_api_getting_started.md ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # MoMo API
2
+
3
+ ## Getting Started
4
+
5
+ **y'ello**, in this section we'll walk you through getting up and running on our Mobile Money Open API. Here you will:
6
+
7
+ * Signup For An Account
8
+ * Subscribe To Our Products
9
+ * Manage Your Subscriptions
10
+ * Generate API User and API Key
11
+
12
+ ### 1. Signup For An Account
13
+
14
+ An account activation link will be sent in an email. The activation link expires within 24 hours of it being sent, and you will need to register for another account.
15
+
16
+ ### 2. Subscribe To Our Products
17
+
18
+ On the products page on our developer portal you should see 4 items you can subscribe to:
19
+
20
+ * Collection Widget
21
+ * Collection
22
+ * Remittances
23
+ * Disbursements
24
+
25
+ Each product will have a brief description, a link to the corresponding documentation and Subscribe button on the Products page.
26
+
27
+ ### 3. Manage Your Subscriptions
28
+
29
+ Developers are issued a **Primary Key** and **Secondary Key** for every product.
30
+
31
+ Both primary and secondary Subscription key provides access to the API. Without one of them a developer cannot access any of the APIs. Subscriptions are stored under the user profile and have no expiry.
32
+
33
+ Here you can view the status of the package, date it started, conduct cancellation or activation actions, and also show or regenerate your Primary Key and Secondary Key
34
+
35
+ ### 4. Generate API User and API Key
36
+
37
+ You are now almost ready to start we building with our Mobile Money Open API. The next thing we need to do is to Provision the API User and API Key using the Sandbox Provisioning API. We do this in the next section.
knowledge_base/mtn_momo/mtn_momo_api_intro.md ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # MoMo API
2
+
3
+ ## Introduction
4
+
5
+ The purpose of this site is to detail the design principles, objects, behaviours and error handling for the MTN Mobile Money API. The overriding goal of the API is to enable all parties to implement MTN Mobile Money APIs in a flexible, yet consistent manner. We hope to achieve this by the implementing the following principles:
6
+
7
+ * Use of REST architectural principles.
8
+ * Providing a set of well-defined objects that are abstracted from the underlying object representations held in the various mobile money systems. This allows an API client to construct an API message without requiring specific knowledge of the target server implementation.
9
+ * Creation of a standard set of transaction types and other key enumerations, removing the need for developers to map for each and every API implementation.
10
+ * Use of ISO international standards for enumerators such as currency and country codes
11
+ * Use of supplementary metadata and sub-types to enable use case and/or mobile money provider-specific properties to be conveyed where necessary.
12
+ * Recognising that no common mobile money account identifier exists, use of a flexible construct to enable the target account(s) and transaction parties to be identified using one or multiple identifier types.
13
+
14
+ The Open API is a JSON REST API that is used by Partner systems to access services in the Wallet platform. The Open API exposes services that are used by e.g. online merchants for managing payments and other financial services. This document gives an overview of the structure of the API.
knowledge_base/mtn_momo/mtn_momo_api_sandbox_use_cases.md ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # MoMo API
2
+
3
+ ## Sandbox Use Cases
4
+
5
+ To facilitate testing a set of predefined users and Test accounts are provided. These users and accounts have a predefined test scenario. A developer needs to Signup and Subscribe to a Product before accessing any of the APIs.
6
+
7
+ In order to test Sandbox Use cases, Merchants/Agents/Partners/Testers needs to use the Sandbox Provisioning Collection and generate the apiuser and apikey and the same needs to be used for testing any of the sandbox Use cases. Existing Partner Accounts created on Partner GUI can't be used for testing the sandbox usecases.
8
+
9
+ ## OAuth Token
10
+
11
+ OAuth Token is generated from the merchants' API Key and Secret. The API Key and API Secret can be obtained through the provisioning API in Sandbox, as described in the API User and API Key Management section.
12
+
13
+ ## Test Environment
14
+
15
+ The Target Environment used in Testing is **'Sandbox'**
16
+
17
+ ## Test Currency
18
+
19
+ The currency used in Sandbox is **EUR**
20
+
21
+ ## Sandbox Use Case
22
+
23
+ The following Numbers are predefined with respective response for all Testcases.
24
+
25
+ | Sandbox Use Case | Testing Values |
26
+ |------------------|----------------|
27
+ | AccountBalanceResponses | Success, AccountNotFound, ZeroBalance, NegativeBalance, NotAllowed, NotAllowedTargetEnvironment, InternalProcessingError, ServiceUnavailable |
28
+ | AccountHolderActiveMsisdnNotFound | 46733123450 |
29
+ | AccountHolderActiveMsisdnNotActive | 46733123451 |
30
+ | AccountHolderActiveMsisdnNotAllowed | 46733123452 |
31
+ | AccountHolderActiveMsisdnNotAllowedTargetEnvironment | 46733123453 |
32
+ | AccountHolderActiveMsisdnInternalProcessingError | 46733123454 |
33
+ | AccountHolderActiveMsisdnServiceUnavailable | 46733123455 |
34
+ | AccountHolderActiveEmailNotFound | notfound@email.com |
35
+ | AccountHolderActiveEmailNotActive | notactive@email.com |
36
+ | AccountHolderActiveEmailNotAllowed | notallowed@email.com |
37
+ | AccountHolderActiveEmailNotAllowedTargetEnvironment | notallowedtargetenvironment@email.com |
38
+ | AccountHolderActiveEmailInternalProcessingError | internalprocessingerror@email.com |
39
+ | AccountHolderActiveEmailServiceUnavailable | serviceunavailable@email.com |
40
+ | AccountHolderActivePartyCodeNotFound | 5cecb5a7-8bd0-4f49-87b1-8eb9ecc7b7bc |
41
+ | AccountHolderActivePartyCodeNotActive | b0040b3c-b426-4a90-af6e-673e65861cd7 |
42
+ | AccountHolderActivePartyCodeNotAllowed | 4d5f500f-c385-4901-9be5-25b6d36ad220 |
43
+ | AccountHolderActivePartyCodeNotAllowedTargetEnvironment | d2265f9b-0c22-496d-908f-79a65ad66266 |
44
+ | AccountHolderActivePartyCodeInternalProcessingError | 8f548a78-ceb8-4d12-a243-5640b91e91a4 |
45
+ | AccountHolderActivePartyCodeServiceUnavailable | 585c07a4-2c80-42f7-9e73-e586b36dee68 |
46
+ | RequestToPayPayerFailed | 46733123450 |
47
+ | RequestToPayPayerRejected | 46733123451 |
48
+ | RequestToPayPayerExpired | 46733123452 |
49
+ | RequestToPayPayerOngoing | 46733123453 |
50
+ | RequestToPayPayerDelayed | 46733123454 |
51
+ | RequestToPayPayerNotFound | 46733123455 |
52
+ | RequestToPayPayerPayeeNotAllowedToReceive | 46733123456 |
53
+ | RequestToPayPayerNotAllowed | 46733123457 |
54
+ | RequestToPayPayerNotAllowedTargetEnvironment | 46733123458 |
55
+ | RequestToPayPayerInvalidCallbackUrlHost | 46733123459 |
56
+ | RequestToPayPayerInvalidCurrency | 46733123460 |
57
+ | RequestToPayPayerInternalProcessingError | 46733123461 |
58
+ | RequestToPayPayerServiceUnavailable | 46733123462 |
59
+ | RequestToPayPayerCouldNotPerformTransaction | 46733123463 |
60
+ | PreApprovalPayerFailed | 46733123450 |
61
+ | PreApprovalPayerRejected | 46733123451 |
62
+ | PreApprovalPayerExpired | 46733123452 |
63
+ | PreApprovalPayerOngoing | 46733123453 |
64
+ | PreApprovalPayerDelayed | 46733123454 |
65
+ | PreApprovalPayerNotFound | 46733123455 |
66
+ | PreApprovalPayerNotAllowed | 46733123456 |
67
+ | PreApprovalPayerNotAllowedTargetEnvironment | 46733123457 |
68
+ | PreApprovalPayerInvalidCallbackUrlHost | 46733123458 |
69
+ | PreApprovalPayerInvalidCurrency | 46733123459 |
70
+ | PreApprovalPayerInternalProcessingError | 46733123460 |
71
+ | PreApprovalPayerServiceUnavailable | 46733123461 |
72
+ | RequestToWithdrawPayerFailed | 46733123450 |
73
+ | RequestToWithdrawPayerRejected | 46733123451 |
74
+ | RequestToWithdrawPayerExpired | 46733123452 |
75
+ | RequestToWithdrawPayerOngoing | 46733123453 |
76
+ | RequestToWithdrawPayerDelayed | 46733123454 |
77
+ | RequestToWithdrawPayerNotFound | 46733123455 |
78
+ | RequestToWithdrawPayerPayeeNotAllowedToReceive | 46733123456 |
79
+ | RequestToWithdrawPayerNotAllowed | 46733123457 |
80
+ | RequestToWithdrawPayerNotAllowedTargetEnvironment | 46733123458 |
81
+ | RequestToWithdrawPayerInvalidCallbackUrlHost | 46733123459 |
82
+ | RequestToWithdrawPayerInvalidCurrency | 46733123460 |
83
+ | RequestToWithdrawPayerInternalProcessingError | 46733123461 |
84
+ | RequestToWithdrawPayerServiceUnavailable | 46733123462 |
85
+ | RequestToWithdrawPayerCouldNotPerformTransaction | 46733123463 |
86
+ | RefundTransactionNotFound | 1 |
87
+ | RefundTransactionFailed | 2 |
88
+ | RefundTransactionRejected | 3 |
89
+ | RefundTransactionExpired | 4 |
90
+ | RefundTransactionOngoing | 5 |
91
+ | RefundTransactionDelayed | 6 |
92
+ | RefundTransactionNotAllowed | 7 |
93
+ | RefundTransactionNotAllowedTargetEnvironment | 8 |
94
+ | RefundTransactionInvalidCallbackUrlHost | 9 |
95
+ | RefundTransactionInvalidCurrency | 10 |
96
+ | RefundTransactionInternalProcessingError | 11 |
97
+ | RefundTransactionServiceUnavailable | 12 |
98
+ | RefundTransactionCouldNotPerformTransaction | 13 |
99
+ | DepositPayerFailed | 46733123450 |
100
+ | DepositPayerRejected | 46733123451 |
101
+ | DepositPayerExpired | 46733123452 |
102
+ | DepositPayerOngoing | 46733123453 |
103
+ | DepositPayerDelayed | 46733123454 |
104
+ | DepositPayerNotFound | 46733123455 |
105
+ | DepositPayerPayeeNotAllowedToReceive | 46733123456 |
106
+ | DepositPayerNotAllowed | 46733123457 |
107
+ | DepositPayerNotAllowedTargetEnvironment | 46733123458 |
108
+ | DepositPayerInvalidCallbackUrlHost | 46733123459 |
109
+ | DepositPayerInvalidCurrency | 46733123460 |
110
+ | DepositPayerInternalProcessingError | 46733123461 |
111
+ | DepositPayerServiceUnavailable | 46733123462 |
112
+ | DepositPayerCouldNotPerformTransaction | 46733123463 |
113
+ | TransferPayeeFailed | 46733123450 |
114
+ | TransferPayeeRejected | 46733123451 |
115
+ | TransferPayeeExpired | 46733123452 |
116
+ | TransferPayeeOngoing | 46733123453 |
117
+ | TransferPayeeDelayed | 46733123454 |
118
+ | TransferPayeeNotEnoughFunds | 46733123455 |
119
+ | TransferPayeePayerLimitReached | 46733123456 |
120
+ | TransferPayeeNotFound | 46733123457 |
121
+ | TransferPayeeNotAllowed | 46733123458 |
122
+ | TransferPayeeNotAllowedTargetEnvironment | 46733123459 |
123
+ | TransferPayeeInvalidCallbackUrlHost | 46733123460 |
124
+ | TransferPayeeInvalidCurrency | 46733123461 |
125
+ | TransferPayeeInternalProcessingError | 46733123462 |
126
+ | TransferPayeeServiceUnavailable | 46733123463 |
127
+ | Oauth2CustomScopes | all_info |
128
+ | Oauth2AccountHolderNotFound | 46733123450 |
129
+ | Oauth2ConsentRejected | 46733123451 |
130
+ | Oauth2ConsentPending | 46733123452 |
131
+ | Oauth2ConsentExpired | 46733123453 |
132
+ | Oauth2ConsentExpiredRefreshToken | 46733123454 |
133
+ | Oauth2ConsentRevoked | 46733123455 |
134
+ | Oauth2ConsentDeletedScope | 46733123456 |
135
+ | Oauth2ConsentAlreadyUsed | 46733123457 |
136
+ | Oauth2FinancialNoFunds | 46733123458 |
137
+ | Oauth2FinancialPayeeNotFound | 46733123459 |
138
+ | Oauth2FinancialConsentMismatch | 46733123460 |
139
+ | Oauth2FinancialPayerLimitReached | 46733123461 |
140
+ | Oauth2FinancialPayeeNotAllowedToReceive | 46733123462 |
141
+ | CashTransferTransactionNotFound | 1 |
142
+ | CashTransferTransactionFailed | 2 |
143
+ | CashTransferTransactionRejected | 3 |
144
+ | CashTransferTransactionExpired | 4 |
145
+ | CashTransferTransactionPayeeNotFound | 5 |
146
+ | CashTransferTransactionPayeeNotAllowedToReceive | 6 |
147
+ | CashTransferTransactionNotAllowed | 7 |
148
+ | CashTransferTransactionNotAllowedTargetEnvironment | 8 |
149
+ | CashTransferTransactionInvalidCallbackUrlHost | 9 |
150
+ | CashTransferTransactionInvalidCurrency | 10 |
151
+ | CashTransferTransactionInternalProcessingError | 11 |
152
+ | CashTransferTransactionServiceUnavailable | 12 |
153
+ | CashTransferTransactionCouldNotPerformTransaction | 13 |
154
+
155
+ **Note:** Any other number results in Success
knowledge_base/mtn_momo/mtn_momo_api_use_cases.md ADDED
@@ -0,0 +1,161 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # MoMo API
2
+
3
+ ## Use Cases
4
+
5
+ Select the product of interest and the use case
6
+
7
+ Follow the APIs as shown. Execute in sequential order.
8
+
9
+ **Product Set:** Select a Product
10
+ **Use Case:** Please select a product first
11
+
12
+ ## Request To Pay
13
+
14
+ Request to Pay service is used for requesting a payment from a customer (Payer). This can be used by e.g. an online web shop to request a payment for a customer. The customer is requested to approve the transaction on the customer client.
15
+
16
+ ### Flow:
17
+
18
+ 1. Customer (Payer) have selected product(s) in the merchant web shop and decided to check out. Customer select to pay with Mobile Money.
19
+
20
+ 2. The provider system collects the account information for the customer e.g. mobile number and calculate the total amount of the products.
21
+
22
+ 3. The provider system sends a request to pay (`POST /requesttopay`) operation to Wallet Platform. This request includes the amount and customer (Payer) account holder number.
23
+
24
+ 4. Wallet Platform will respond with HTTP 202 Accepted to the provider system
25
+
26
+ 5. Provider shall inform the customer that a payment needs to be approved, by giving information on the merchant web page. For example, the merchant could show information that payment is being processed and that customer needs to approve using the own client, e.g. USSD, mobile app.
27
+
28
+ 6. Wallet Platform will process the request so that the customer can approve the payment. The request to pay will be in PENDING state until the customer have approved/Rejected the payment.
29
+
30
+ 7. The Customer (Payer) will use his/her own client to review the payment. Customer can approve or reject the payment.
31
+
32
+ 8. Wallet platform will transfer the funds if the customer approves the payment. Status of the payment is updated to SUCCESSFUL or FAILED.
33
+
34
+ 9. If a callback URL was provided in the `POST /requesttopay` then a callback will be sent once the request to pay have reached a final state (SUCCESSFUL, FAILED). Note the callback will only be sent once. There is no retry.
35
+
36
+ 10. GET request can be used for validating the status of the transaction. GET is used if the partner system has not requested a callback by providing a callback URL or if the callback was not received.
37
+
38
+ ## Pre-Approval
39
+
40
+ Pre-approval is used to setup an auto debit towards a customer. The Partner can request a pre-approval from the customer. Once the customer has approved then the partner can debit the customer account without authorization from the customer.
41
+
42
+ The call flow for setting up a pre-approval is like the request to pay use case. The following picture describes the sequence for pre-approval.
43
+
44
+ ### Flow:
45
+
46
+ 1. The Provider sends a `POST /preapproval` request to Wallet platform.
47
+
48
+ 2. Provider shall inform the customer that pre-approval needs to be approved.
49
+
50
+ 3. Customer (Payer) will use the own client to view the pre-approval request. Customer can approve or reject the request.
51
+
52
+ 4. Callback will be sent if a callback URL was provided in the POST request. The callback is sent when the request has reach a final state (Successful, Failed).
53
+
54
+ 5. The Provider can use the GET request to validate the status of the pre-approval.
55
+
56
+ ## Transfer
57
+
58
+ Transfer is used for transferring money from the provider account to a customer.
59
+
60
+ The below sequence gives an overview of the flow of the transfer use case.
61
+
62
+ ### Flow:
63
+
64
+ 1. The Provider sends a `POST /transfer` request to Wallet platform.
65
+
66
+ 2. Wallet platform will directly respond to indicate that the request is received and will be processed.
67
+
68
+ 3. Wallet platform will authorize the request to ensure that the transfer is allowed. The funds will be transferred from the provider account to the Payee account provided in the transfer request.
69
+
70
+ 4. Callback will be sent if a callback URL was provided in the POST request. The callback is sent when the request has reach a final state (SUCCESSFUL, FAILED).
71
+
72
+ 5. The Provider can use the GET request to validate the status of the transfer.
73
+
74
+ ## Validate Account Holder
75
+
76
+ Validate account holder can be used to do a validation if a customer is active and able to receive funds. The use case will only validate that the customer is available and active. It does not validate that a specific amount can be received.
77
+
78
+ The sequence for the validate account holder is described below.
79
+
80
+ ### Flow:
81
+
82
+ 1. The Partner can send a `GET /accountholder` request to validate is a customer is active. The Partner provides the id of that customer as part of the URL
83
+
84
+ 2. Wallet platform will respond with HTTP 200 if the account holder is active.
85
+
86
+ ## Get Balance
87
+
88
+ Get balance request is used to check the balance on the default account connected to the API User. The following is the sequence flow for get balance use case.
89
+
90
+ ## Delivery Notification
91
+
92
+ This service is intended to provide an additional notification to a customer after the completion of a successful financial transaction, by SMS or by email.
93
+
94
+ Merchants and Service Providers using **PGW** (Partner gateway is an application responsible for connecting API manager to different Wallet instances across all the Opcos) to interact with the Wallet Platform have the capability to send a notification to their customers after completed transaction.
95
+
96
+ Additional information is sent via SMS and is free text that may contain information that a partner wants to communicate for example, delivery notification reference number, a lottery number, a booking number, ticket id etc.
97
+
98
+ The channel used to send the notification will be determined based on what identity was used to initiate original transaction/payment.
99
+
100
+ The time window during which partner can use additional notification is configured in Partner Gateway.
101
+
102
+ ## Validate Consumer Identity
103
+
104
+ The Validate Consumer Identity use case (**KYC as a service**) enables a partner to retrieve (limited) customer KYC information with their consent. Upon request by the service provider, the customer will authorize, authenticate and provide consent to get detailed information.
105
+
106
+ The Partner will receive a short-lived token to fetch detailed information of the customer from the wallet platform.
107
+
108
+ The basic information that can be retrieved by default is Profile: Name, Gender, Date of birth, locale etc.
109
+
110
+ API: **GetBasicUserinfo** can be used to either validate for example, Consumer's age or name.
111
+
112
+ The use case will return limited information about the Consumer that can be used to pre-populate fields, validate the Consumer's age or use it for remittance or sanctions screening purposes.
113
+
114
+ ## Get Consumer Information with Consent
115
+
116
+ This use case (**Authentication as a Service**) is used to validate/authenticate customer information, by way of a service provider sending a request to the wallet platform, with or without customer consent. The consumer will authenticate via authorization service, and if required, the consumer will be required to provide consent. The scope is to validate or retrieve customer information.
117
+
118
+ This use case will also enable a partner to obtain consent, or digitally accept terms and conditions. This can be seen as a "digital signature" (where a partner requires this).
119
+
120
+ A partner may also be able to fetch limited user info without requiring customer consent. This will also enable partners to record and review that a customer has accepted the terms of a contract (amongst other use cases).
121
+
122
+ This will be used as a base when any token based "as a service" is required.
123
+
124
+ API: **bc-authorize**.
125
+
126
+ It provides the Partner with the ability to fetch detailed information about a Consumer, after the Consumer has given the Partner consent for the retrieval of said information:
127
+
128
+ ## Token Based API Authorization
129
+
130
+ Token based API authorization is required to support consent management for different services. The Partner will request consent from the consumer for certain financial and non-financial transactions, and this will remain valid until the account holder revokes the consent.
131
+
132
+ The functionalities that can be enhanced by using consent will be:
133
+
134
+ 1. Transfer
135
+ 2. Payment
136
+ 3. Merchant payment
137
+ 4. Transfer to any bank account
138
+
139
+ For partners, this will enable them to view the status of the transactions for which consent was given using the related parameters. For account holders, they can revoke the consent from any service at their will.
140
+
141
+ Business benefit of consent can be improved customer experience. Consent can be configured for a specific time and there will be no need to get consent for each transaction. Providing better control on what services are being allowed by the account holder for consent etc.
142
+
143
+ ## Transfer with Consumer Consent
144
+
145
+ Gives the partner the ability to transfer money on behalf of a consumer, after the consumer has given the partner consent to do so.
146
+
147
+ ## Merchant Payment with Consumer Consent
148
+
149
+ Gives the Merchant System (partner) the possibility to perform a merchant payment on behalf of a consumer to any receiver. This will only be possible after the Consumer has given the Merchant System (partner) a consent for doing so.
150
+
151
+ ## Payment with Consumer Consent
152
+
153
+ Gives the Merchant System (partner) the possibility to perform a payment on behalf of a consumer to any receiver. This will only be possible after the Consumer has given the Merchant System (partner) a consent for doing so.
154
+
155
+ Payment with consent will be similar to Merchant Payment. For more details, Please refer above section- "Merchant Payment with Consumer Consent".
156
+
157
+ ## Bank Transfer with Consumer Consent
158
+
159
+ Gives the Merchant System (partner) the possibility to perform a Bank Transfer on behalf of a consumer to any receiver. This will only be possible after the Consumer has given the Merchant System (partner) a consent for doing so.
160
+
161
+ Bank transfer with consent will be similar to Transfer with Consent. For more details, Please refer above section- "Transfer with Consumer Consent".
knowledge_base/mtn_momo/mtn_momo_api_user_and_key_management.md ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # MoMo API Documentation
2
+
3
+ ## API User and Key Management
4
+
5
+ ### Authentication
6
+
7
+ There are two credentials used in the Open API:
8
+
9
+ 1. **Subscription Key**
10
+ 2. **API User and API Key**
11
+
12
+ The subscription key is used to give access to APIs in the API Manager portal. A user is assigned a subscription key when the user subscribes to products in the API Manager Portal.
13
+
14
+ The API User and API Key are used to grant access to the wallet system in a specific country. API user and Key are wholly managed by the user through Partner Portal.
15
+
16
+ Users are allowed to generate/revoke API Keys through the Partner Portal.
17
+
18
+ However, on Sandbox Environment a Provisioning API is exposed to enable users to generate their own API User and API Key for testing purposes only.
19
+
20
+ ### Subscription Key
21
+
22
+ The subscription key is part of the header of all requests sent to the API Manager. The subscription key can be found under user profile in the API Manager Portal.
23
+
24
+ The subscription key is assigned to the `Ocp-Apim-Subscription-Key` parameter of the header.
25
+
26
+ ### API User and API Key for OAuth 2.0
27
+
28
+ The API user and API key are provisioned differently in the sandbox and production environment.
29
+
30
+ In the Sandbox a provisioning API is used to create the API User and API Key, whereas in the production environment the provisioning is done through the User Portal.
31
+
32
+ The sections below describe the different steps required in creating API User and API key in Sandbox and Production Environments.
33
+
34
+ ## Create API User
35
+
36
+ 1. The Provider sends a `POST {baseURL}/apiuser` request to Wallet platform.
37
+ 2. The Provider specifies the UUID Reference ID in the request Header and the subscription Key
38
+ 3. Reference ID will be used as the User ID for the API user to be created.
39
+ 4. Wallet Platform creates the User and responds with 201
40
+
41
+ ### Example
42
+
43
+ **Request**
44
+ ```http
45
+ POST {baseURL}/apiuser HTTP/1.1
46
+ Host: momodeveloper.mtn.com
47
+ X-Reference-Id: c72025f5-5cd1-4630-99e4-8ba4722fad56
48
+ Ocp-Apim-Subscription-Key: d484a1f0d34f4301916d0f2c9e9106a2
49
+
50
+ {
51
+ "providerCallbackHost": "clinic.com"
52
+ }
53
+ ```
54
+
55
+ **Response**
56
+ ```http
57
+ 201 Created
58
+ ```
59
+
60
+ ## Create API Key
61
+
62
+ 1. The Provider sends a `POST {baseURL}/apiuser/{APIUser}/apikey` request to Wallet platform.
63
+ 2. The Provider specifies the API User in the URL and subscription Key in the header.
64
+ 3. Wallet Platform creates the API Key and responds with 201 Created with the newly Created API Key in the Body.
65
+ 4. Provider now has both API User and API Key created.
66
+
67
+ ### Example
68
+
69
+ **Request**
70
+ ```http
71
+ POST {baseURL}/apiuser/c72025f5-5cd1-4630-99e4-8ba4722fad56/apikey HTTP/1.1
72
+ Host: momodeveloper.mtn.com
73
+ Ocp-Apim-Subscription-Key: d484a1f0d34f4301916d0f2c9e9106a2
74
+ ```
75
+
76
+ **Response**
77
+ ```http
78
+ HTTP/1.1 201 Created
79
+ Date: Wed, 10 Oct 2018 09:16:15 GMT
80
+ Content-Type: application/json;charset=utf-8
81
+ Content-Length: 45
82
+
83
+ {
84
+ "apiKey": "f1db798c98df4bcf83b538175893bbf0"
85
+ }
86
+ ```
87
+
88
+ ## GET API User Details
89
+
90
+ It is possible to fetch API user details such as Call Back Host. However, it is not possible to fetch the API key. Provider shall be required to generate a new Key should they lose the existing one.
91
+
92
+ 1. The Provider sends a `GET {baseURL}/apiuser/{APIUser}` request to Wallet platform.
93
+ 2. The Provider specifies the API User in the URL and subscription Key in the header.
94
+ 3. Wallet Platform responds with 200 OK and details of the user.
95
+
96
+ **Note:** TargetEnvironment is preconfigured to sandbox in the Sandbox environment, therefore Providers will not have the option of setting it to a different parameter.
97
+
98
+ ### Example
99
+
100
+ **Request**
101
+ ```http
102
+ GET {baseURL}/apiuser/c72025f5-5cd1-4630-99e4-8ba4722fad56
103
+ Host: momodeveloper.mtn.com
104
+ Ocp-Apim-Subscription-Key: d484a1f0d34f4301916d0f2c9e9106a2
105
+ ```
106
+
107
+ **Response**
108
+ ```http
109
+ HTTP/1.1 200 Accepted
110
+ Date: Wed, 10 Oct 2018 09:16:15 GMT
111
+
112
+ {
113
+ "providerCallbackHost": "clinic.com",
114
+ "targetEnvironment": "sandbox"
115
+ }
116
+ ```
117
+
118
+ ## OAuth 2.0
119
+
120
+ The Open API uses OAuth 2.0 token for authentication of request. User will request an access token using Client Credential Grant according to RFC 6749. The token received is according to RFC 6750 Bearer Token.
121
+
122
+ The API user and API key are used in the basic authentication header when requesting the access token. The API user and key are managed in the Partner GUI for the country where the account is located. The Partner can create and manage API user and key from the Partner GUI.
123
+
124
+ In the Sandbox, the API Key and API User are managed through the Provisioning API.
125
+
126
+ The received token has an expiry time. The same token can be used for transactions until it expires. A new token is requested by using the `POST /token` service in the same way as the initial token. The new token can be requested for before the previous one has expired to avoid authentication failure due to expired token.
127
+
128
+ **Important:** The token must be treated as a credential and kept secret. The party that have access to the token will be authenticated as the user that requested the token.
129
+
130
+ ### Authentication Flow
131
+
132
+ The below sequence describes the flow for requesting a token and using the token in a request:
133
+
134
+ 1. Provider system requests an access token using the API Key and API user as authentication.
135
+ 2. Wallet platform authenticates credentials and responds with the access token
136
+ 3. Provider system will use the access token for any request that is sent to Wallet Platform, e.g. `POST /requesttopay`
137
+
138
+ **Note:** The same token shall be used if it is not expired.
139
+
140
+ ## API Methods
141
+
142
+ The API uses POST, GET, PUT methods. This section gives an overview of the interaction sequence used in the API and the usage of the methods.
143
+
144
+ ### POST
145
+
146
+ POST method is used for creating a resource in Wallet Platform. The request includes a reference id which is used to uniquely identify the specific resource that are created by the POST request. If a POST is using a reference id that is already used, then a duplication error response will be sent to the client.
147
+
148
+ **Example:** `POST /requesttopay`
149
+
150
+ The POST is an asynchronous method. The Wallet Platform will validate the request to ensure that it is correct according to the API specification and then answer with HTTP 202 Accepted. The created resource will get status PENDING. Once the request has been processed the status will be updated to SUCCESSFUL or FAILED. The requester may then be notified of the final status through callback.
151
+
152
+ ### GET
153
+
154
+ GET is used for requesting information about a specific resource. The URL in the GET shall include the reference of the resource. If a resource was created with POST then the reference id that was provided in the request is used as the identity of the resource.
155
+
156
+ **Example:**
157
+ - `POST /requesttopay` request is sent with `X-Reference-Id = 11377cbe-374c-43f6-a019-4fb70e57b617`
158
+ - `GET /requesttopay/11377cbe-374c-43f6-a019-4fb70e57b617` will return the status of the request.
159
+
160
+ ### PUT
161
+
162
+ The PUT method is used by the Open API when sending callbacks. Callback is sent if a callback URL is included in the POST request. The Wallet Platform will only send the callback once. There is no retry on the callback if the Partner system does not respond. If the callback is not received, then the Partner system can use GET to validate the status.
knowledge_base/mtn_momo/mtn_momo_error_codes.md ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Error Codes
2
+
3
+ MoMo OpenAPI Team is thrilled to announce that we've been listening to your feedback, and as a result, we've made significant updates to improve the failure handling with MoMoOpenAPI experience for developers, businesses, and customers.
4
+
5
+ ## Improved Failure Reasons and Error Codes
6
+
7
+ In our ongoing effort to provide a seamless and intuitive experience, we've revamped our failure reasons and error codes. These updates ensure that error messages are clearer, more concise, and provide actionable insights. This enhancement enables developers and businesses to quickly identify and resolve issues, reducing friction and improving overall efficiency.
8
+
9
+ ## Benefits for all
10
+
11
+ These updates bring numerous benefits to the MoMoOpenAPI ecosystem, including:
12
+
13
+ 1. **Enhanced Customer Experience:** Improved failure reasons and new actions lead to better, more seamless customer experiences.
14
+ 2. **Increased Efficiency:** Developers and businesses can now manage MoMo customer experiences more efficiently, reducing time and resources spent on issue resolution.
15
+ 3. **Reduced Friction:** Updated error codes and messages reduce friction, making it easier to resolve issues and get back to business as usual.
16
+
17
+ ## Common Error Codes
18
+
19
+ ### NOT_ENOUGH_FUNDS
20
+ **Description:** The payer does not have enough funds.
21
+
22
+ **Action:** Notify the payer of the total amount with fees before initiating debit, and also to ensure availability of the MoMo funds
23
+
24
+ ### PAYER_NOT_FOUND
25
+ **Description:** Payer does not exist
26
+
27
+ **Action:** Before initiating debit, use the Get accountholder Status to confirm the payer has an active MoMo Wallet
28
+
29
+ ### TRANSACTION_NOT_FOUND
30
+ **Description:** The transaction could not be found
31
+
32
+ **Action:** The transaction could not be found
33
+
34
+ ### PAYEE_NOT_ALLOWED_TO_RECEIVE
35
+ **Description:** The payee is unable to receive funds.
36
+
37
+ **Action:** The payee is unable to receive funds, due to a non active wallet status. If encountered request customer for an alternative MoMo number.
38
+
39
+ ### SENDER_ACCOUNT_NOT_ACTIVE
40
+ **Description:** SENDER ACCOUNT NOT ACTIVE
41
+
42
+ **Action:** The MoMo wallet used for the transfer has not been activated. Please contact your account manager in your respective country to activate the wallet for transactions
43
+
44
+ ### COULD_NOT_PERFORM_TRANSACTION
45
+ **Description:** Transaction Not Completed
46
+
47
+ **Action:** Debit transactions have a 5-minute approval window. If the transaction times out, please retry the transaction to complete the debit
48
+
49
+ ### PAYER_LIMIT_REACHED
50
+ **Description:** The payer's limit has been breached.
51
+
52
+ **Action:** Customer's wallet has reached its set limits for debits, which vary by country. To resolve this, the customer must either reduce the debit amount or use another MoMo wallet
53
+
54
+ ### PAYEE_LIMIT_REACHED
55
+ **Description:** The payee's limit has been breached.
56
+
57
+ **Action:** Customer's wallet has reached its set limits for transfers, which vary by country. To resolve this, the customer must either reduce the transfer amount or share another MoMo wallet
58
+
59
+ ### RESOURCE_ALREADY_EXIST
60
+ **Description:** Duplicated reference id. Creation of resource failed.
61
+
62
+ **Action:** Error implies that the X-Reference-Id header in your financial API POST request is duplicated To resolve this issue, you need to ensure that each financial API POST request has a unique X-Reference-Id header in UUID Version 4 format.
63
+
64
+ ### PAYEE_NOT_FOUND
65
+ **Description:** Payee does not active
66
+
67
+ **Action:** Error generated for Debit requests when the business account is blocked
68
+
69
+ ### VALIDATION_ERROR
70
+ **Description:** Request failing Validation
71
+
72
+ **Action:** Validation failures can occur due to incorrect data types, unsupported decimals, empty fields, incorrect field lengths, unsupported currencies, and invalid payer messages or notes (which cannot be null and must be within 128 characters).
knowledge_base/mtn_momo/mtn_momo_open_apis_ sandbox_documentation.md ADDED
@@ -0,0 +1,1358 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # MoMo Open APIs SandBox Documentation
2
+
3
+ MTN MoMo Open APIs are a set of APIs that allow third-party developers to create innovative digital financial services using MTN Mobile Money platform.
4
+
5
+ These APIs facilitate all key use cases including consumer to business payments (C2B), business to business payments (B2B), collections, and disbursements, Cash In, Cash Out, Refund, Notification and more.
6
+
7
+ MoMo APIs are completely RESTful and all our responses are returned in JSON.
8
+
9
+ ## Generate Subscription Keys
10
+
11
+ 1. Sign up at https://momodeveloper.mtn.com
12
+ 2. Navigate to the products page Product-descriptions
13
+ 3. Select drop down on product that suits the business case and subscribe
14
+ 4. After completion, you can locate the Subscription Keys in your profile
15
+
16
+ **Configure the Environment variables as shown below**
17
+
18
+ | Subscription name | Key Type | Variable |
19
+ |------------------|----------|----------|
20
+ | Disbursements | Primary | {{Disbursement_Subscription-Key}} |
21
+ | Collections | Primary | {{Collection_Subscription-Key}} |
22
+
23
+ ## Fork the MoMo Open API Postman Collection
24
+
25
+ Ensure the Environment is set to sandbox before running the collection.
26
+
27
+ The Collections and Folders have been configured with basic testing scripts to help narrow down the error scope.
28
+
29
+ ## Generate API User and Key SandBox
30
+
31
+ Run the below request to generate API user and Key. These will be auto saved within the environment leveraging on scripting.
32
+
33
+ ## Test MSISDN Numbers
34
+
35
+ In the Sandbox, adjust the Test Numbers(MSISDN) within the sandbox environment as follows.
36
+
37
+ Each numeric string yields a distinct response status within the Sandbox.
38
+
39
+ For Production, Number begins with the country code.
40
+
41
+ | Number - {{MSISDN}} | Expected Status Response |
42
+ |---------------------|-------------------------|
43
+ | 46733123450 | Failed |
44
+ | 46733123451 | Rejected |
45
+ | 46733123452 | Timeout |
46
+ | 56733123453 | Success |
47
+ | 46733123454 | Pending |
48
+
49
+ ## Authorization
50
+
51
+ Consists of Bearer Access Token API, This is also saved within the environment, will refresh once its time has expired, 60 minutes.
52
+
53
+ ### Generate access_token
54
+
55
+ **Endpoint:** `POST {{base_url}}/collection/token/`
56
+
57
+ The POST /collection/token/ endpoint is used to obtain a token.
58
+
59
+ **Request Body**
60
+ No request body parameters required for this request.
61
+
62
+ **Response**
63
+ Upon a successful request, the response will be a JSON object with the following properties:
64
+
65
+ - **access_token** (string): The access token obtained
66
+ - **token_type** (string): The type of token obtained
67
+ - **expires_in** (integer): The duration in seconds for which the token is valid
68
+
69
+ **Headers**
70
+ No specific headers are required for this request.
71
+
72
+ **Example response:**
73
+
74
+ ```json
75
+ {
76
+ "access_token": "",
77
+ "token_type": "",
78
+ "expires_in": 0
79
+ }
80
+ ```
81
+
82
+ **Authorization**
83
+ - Basic Auth
84
+ - Username: `{{api_user}}`
85
+ - Password: `{{api_key}}`
86
+
87
+ **Example Request**
88
+
89
+ ```bash
90
+ curl --location --globoff --request POST '{{base_url}}/collection/token/' \
91
+ --header 'Ocp-Apim-Subscription-Key: {{Collection_Subscription-Key}}' \
92
+ --data ''
93
+ ```
94
+
95
+ **Example Response**
96
+
97
+ ```json
98
+ {
99
+ "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSMjU2In0.eyJjbGllbnRJZCI6IjA0ZGIwYjk5LTkyNmYtNGQ0Ni04YzI3LTQ2OWIwMmMxZWUxMyIsImV4cGlyZXMiOiIyMDI0LTEwLTA4VDIxOjI1OjI3LjgwNyIsInNlc3Npb25JZCI6IjVhODkwODJlLTk3NmQtNGYwMy04OTNjLTJlNTEwNWI1YWM5NiJ9.hAmAJFZkdyhuEEWGgdZZUtO5hYzIcigV-Sjrhv_zsmTBRu8X8Dhs5Zssr-mulwSaDjITcKRlCna1Eej5kJNQUYeLDDB5pKK5Xx3T3eFxNLrTNvGABMENfoWZgmlLnBgg_ux9bmm4XcncNL5pb_cpEgCZKVpv-SJh2dTtY8Q-QutUDNDG34m-ifMcwicK59RiGsF8fNPKy44wX_BJhqwrg80FYEhOhrqJhzVqSSLb807yIQkx5fNFy8ekzrR2toGusQBsAVwz06g3xaDVMGXBz7mmB35gCP_NQscIGSjtFXTNeItM-HJzTU7TDBzbMDMuRaI2DU1mb7Dt30z8D2NNrw",
100
+ "token_type": "access_token",
101
+ "expires_in": 3600
102
+ }
103
+ ```
104
+
105
+ ## Get Paid
106
+
107
+ MoMo get paid APIs enable businesses to collect payments from consumers and businesses in different ways.
108
+ Partners initiate instant payment within any digital platform (website, mobile application) through MoMo channels.
109
+ Folder also includes a set of capabilities that enhance value and offer an excellent customer experience. These include: Refund, Notification.
110
+
111
+ ### Request To Pay
112
+
113
+ **Endpoint:** `POST {{base_url}}/collection/v1_0/requesttopay`
114
+
115
+ Create Request To Pay - This endpoint allows the client to initiate a request to pay.
116
+
117
+ **Request Body**
118
+ - **amount** (string): The amount to be requested
119
+ - **currency** (string): The currency in which the amount is requested
120
+ - **externalId** (string): The unique identifier for the request
121
+ - **payer** (object):
122
+ - **partyIdType** (string): The type of party ID for the payer (MSISDN/ALIAS/EMAIL)
123
+ - **partyId** (string): The ID of the payer
124
+ - **payerMessage** (string): Message from the payer
125
+ - **payeeNote** (string): Note for the payee
126
+
127
+ **Request Body Example**
128
+
129
+ ```json
130
+ {
131
+ "amount": "600",
132
+ "currency": "EUR",
133
+ "externalId": "00004335",
134
+ "payer": {
135
+ "partyIdType": "MSISDN",
136
+ "partyId": "{{MSISDN}}"
137
+ },
138
+ "payerMessage": "MoMo Market Payment",
139
+ "payeeNote": "MoMo Market Payment"
140
+ }
141
+ ```
142
+
143
+ **Authorization**
144
+ - Bearer Token: `{{Access_Token}}`
145
+
146
+ **Example Request**
147
+
148
+ ```bash
149
+ curl --location 'https://sandbox.momodeveloper.mtn.com/collection/v1_0/requesttopay' \
150
+ --header 'X-Reference-Id: 2f209631-07d4-4254-8d6a-bd0f1ef78538' \
151
+ --header 'X-Target-Environment: sandbox' \
152
+ --header 'Content-Type: application/json' \
153
+ --header 'Ocp-Apim-Subscription-Key: bca0c92d326a46cd885f443d51b859f1' \
154
+ --header 'X-Callback-Url: https://webhook.site/mywebhooksandbox' \
155
+ --data '{
156
+ "amount": "600",
157
+ "currency": "EUR",
158
+ "externalId": "00004335",
159
+ "payer": {
160
+ "partyIdType": "MSISDN",
161
+ "partyId": "56733123450"
162
+ },
163
+ "payerMessage": "MoMo Market Payment",
164
+ "payeeNote": "MoMo Market Payment"
165
+ }'
166
+ ```
167
+
168
+ ### Status Request To Pay
169
+
170
+ **Endpoint:** `GET {{base_url}}/collection/v1_0/requesttopay/:Request_ID_Debit`
171
+
172
+ Retrieve Request To Pay Response - This endpoint retrieves the response for a specific request to pay, identified by the unique Request_ID_Debit.
173
+
174
+ **Authorization**
175
+ - Bearer Token: `{{Access_Token}}`
176
+
177
+ **Path Variables**
178
+ - **Request_ID_Debit**: `{{Request_ID_Debit}}` - Request Id of the debit request Posted
179
+
180
+ **Example Request**
181
+
182
+ ```bash
183
+ curl --location 'https://sandbox.momodeveloper.mtn.com/collection/v1_0/requesttopay/2f209631-07d4-4254-8d6a-bd0f1ef78538' \
184
+ --header 'X-Target-Environment: sandbox' \
185
+ --header 'Ocp-Apim-Subscription-Key: bca0c92d326a46cd885f443d51b859f1'
186
+ ```
187
+
188
+ ### Notification
189
+
190
+ **Endpoint:** `POST {{base_url}}/collection/v1_0/requesttopay/{{Request_ID_Debit}}/deliverynotification`
191
+
192
+ This endpoint is used to send a delivery notification for a specific request to pay.
193
+
194
+ **Request Body**
195
+ - **notificationMessage**: (string) The message for the delivery notification
196
+
197
+ **Request Body Example**
198
+
199
+ ```json
200
+ {
201
+ "notificationMessage": "Thank You for using MoMo"
202
+ }
203
+ ```
204
+
205
+ **Authorization**
206
+ - Bearer Token: `{{Access_Token}}`
207
+
208
+ **Example Request**
209
+
210
+ ```bash
211
+ curl --location 'https://sandbox.momodeveloper.mtn.com/collection/v1_0/requesttopay/2f209631-07d4-4254-8d6a-bd0f1ef78538/deliverynotification' \
212
+ --header 'X-Target-Environment: sandbox' \
213
+ --header 'Content-Type: application/json' \
214
+ --header 'Ocp-Apim-Subscription-Key: bca0c92d326a46cd885f443d51b859f1' \
215
+ --data '{
216
+ "notificationMessage": "Thank You for using MoMo"
217
+ }'
218
+ ```
219
+
220
+ ### Refund
221
+
222
+ **Endpoint:** `POST {{base_url}}/disbursement/v1_0/refund`
223
+
224
+ Refund Disbursement - This endpoint allows the user to initiate a refund for a previous disbursement.
225
+
226
+ **Request Body**
227
+ - **amount** (string): The amount to be refunded
228
+ - **currency** (string): The currency in which the refund amount is specified
229
+ - **externalId** (string): The external identifier for the refund transaction
230
+ - **payerMessage** (string): Message to be displayed to the payer (optional)
231
+ - **payeeNote** (string): Note to be displayed to the payee (optional)
232
+ - **referenceIdToRefund** (string): The reference ID of the disbursement to be refunded
233
+
234
+ **Request Body Example**
235
+
236
+ ```json
237
+ {
238
+ "amount": "300",
239
+ "currency": "EUR",
240
+ "externalId": "Refund_uibi",
241
+ "payerMessage": "Refund for MoMo Market Payment",
242
+ "payeeNote": "Refund for MoMo Market Payment",
243
+ "referenceIdToRefund": "{{Request_ID_Debit}}"
244
+ }
245
+ ```
246
+
247
+ **Authorization**
248
+ - Bearer Token: `{{Access_Token}}`
249
+
250
+ **Example Request**
251
+
252
+ ```bash
253
+ curl --location 'https://sandbox.momodeveloper.mtn.com/disbursement/v1_0/refund' \
254
+ --header 'X-Callback-Url: https://webhook.site/mywebhooksandbox' \
255
+ --header 'X-Reference-Id: 85c66b88-abb8-4523-a81d-2d5566816c70' \
256
+ --header 'X-Target-Environment: sandbox' \
257
+ --header 'Content-Type: application/json' \
258
+ --header 'Ocp-Apim-Subscription-Key: 0fd9826a39aa468fb311d23fc08a55fc' \
259
+ --data '{
260
+ "amount": "300",
261
+ "currency": "EUR",
262
+ "externalId": "Refund_uibi",
263
+ "payerMessage": "Refund for MoMo Market Payment",
264
+ "payeeNote": "Refund for MoMo Market Payment",
265
+ "referenceIdToRefund": "2f209631-07d4-4254-8d6a-bd0f1ef78538"
266
+ }'
267
+ ```
268
+
269
+ ### Refund Status
270
+
271
+ **Endpoint:** `GET {{base_url}}/disbursement/v1_0/refund/:Request_ID_Refund`
272
+
273
+ This endpoint is used to check the status of a refund by providing the Request_ID_Refund, which is also mapped as the X-Reference-Id in the Refund Request.
274
+
275
+ **Headers**
276
+ - **X-Target-Environment** (string, required): The identifier specifying the country the request is meant for
277
+ - **Ocp-Apim-Subscription-Key** (string, required): Subscription key providing access to this API
278
+
279
+ **Authorization**
280
+ - Bearer Token: `{{Access_Token}}`
281
+
282
+ **Path Variables**
283
+ - **Request_ID_Refund**: `{{Request_ID_Refund}}`
284
+
285
+ **Example Request**
286
+
287
+ ```bash
288
+ curl --location 'https://sandbox.momodeveloper.mtn.com/disbursement/v1_0/refund/85c66b88-abb8-4523-a81d-2d5566816c70' \
289
+ --header 'X-Target-Environment: sandbox' \
290
+ --header 'Ocp-Apim-Subscription-Key: 0fd9826a39aa468fb311d23fc08a55fc'
291
+ ```
292
+
293
+ ## Payment
294
+
295
+ Payment API enables a Business to streamline utility payments and initiate services like airtime and bundle activation through MoMo.
296
+
297
+ ### Create Payment
298
+
299
+ **Endpoint:** `POST https://sandbox.momodeveloper.mtn.com/collection/v2_0/payment`
300
+
301
+ This API endpoint is used to initiate a payment collection.
302
+
303
+ **Request Body**
304
+ - **externalTransactionId** (text): The ID of the external transaction
305
+ - **money.amount** (text): The amount of the payment
306
+ - **money.currency** (text): The currency of the payment
307
+ - **customerReference** (text): Reference to the customer initiating the payment
308
+ - **serviceProviderUserName** (text): Username of the service provider
309
+ - **couponId** (text): ID of the coupon, if applicable
310
+ - **productId** (text): ID of the product
311
+ - **productOfferingId** (text): ID of the product offering
312
+ - **receiverMessage** (text): Message for the receiver of the payment
313
+ - **senderNote** (text): Note from the sender of the payment
314
+ - **maxNumberOfRetries** (text): Maximum number of retries for the payment
315
+ - **includeSenderCharges** (text): Indicator whether to include sender charges
316
+
317
+ **Request Body Example**
318
+
319
+ ```json
320
+ {
321
+ "externalTransactionId": "457373",
322
+ "money": {
323
+ "amount": "2000",
324
+ "currency": "EUR"
325
+ },
326
+ "customerReference": "561551442",
327
+ "serviceProviderUserName": "WaterProvider",
328
+ "couponId": "203",
329
+ "productId": "Monthly Payments",
330
+ "productOfferingId": "788",
331
+ "receiverMessage": "Thank You ",
332
+ "senderNote": "Thank You",
333
+ "maxNumberOfRetries": "2",
334
+ "includeSenderCharges": "1"
335
+ }
336
+ ```
337
+
338
+ **Authorization**
339
+ - Bearer Token: `{{Access_Token}}`
340
+
341
+ **Example Request**
342
+
343
+ ```bash
344
+ curl --location 'https://sandbox.momodeveloper.mtn.com/collection/v2_0/payment' \
345
+ --header 'X-Callback-Url: https://webhook.site/mywebhooksandbox' \
346
+ --header 'X-Reference-Id;' \
347
+ --header 'X-Target-Environment: sandbox' \
348
+ --header 'Content-Type: application/json' \
349
+ --header 'Ocp-Apim-Subscription-Key: bca0c92d326a46cd885f443d51b859f1' \
350
+ --data '{
351
+ "externalTransactionId": "457373",
352
+ "money": {
353
+ "amount": "2000",
354
+ "currency": "EUR"
355
+ },
356
+ "customerReference": "561551442",
357
+ "serviceProviderUserName": "WaterProvider",
358
+ "couponId": "203",
359
+ "productId": "Monthly Payments",
360
+ "productOfferingId": "788",
361
+ "receiverMessage": "Thank You ",
362
+ "senderNote": "Thank You",
363
+ "maxNumberOfRetries": "2",
364
+ "includeSenderCharges": "1"
365
+ }'
366
+ ```
367
+
368
+ ### Get Payment Status
369
+
370
+ **Endpoint:** `GET https://sandbox.momodeveloper.mtn.com/collection/v2_0/payment/:Request_ID_Payment`
371
+
372
+ Retrieve Payment Response - This endpoint retrieves the response for a specific payment request identified by the provided Request ID.
373
+
374
+ **Response**
375
+
376
+ ```json
377
+ {
378
+ "referenceId":"",
379
+ "status":"",
380
+ "reason":""
381
+ }
382
+ ```
383
+
384
+ **Authorization**
385
+ - Bearer Token: `{{Access_Token}}`
386
+
387
+ **Path Variables**
388
+ - **Request_ID_Payment**: `{{Request_ID_Payment}}`
389
+
390
+ **Example Request**
391
+
392
+ ```bash
393
+ curl --location 'https://sandbox.momodeveloper.mtn.com/collection/v2_0/payment/:Request_ID_Payment' \
394
+ --header 'X-Target-Environment: sandbox' \
395
+ --header 'Ocp-Apim-Subscription-Key: bca0c92d326a46cd885f443d51b859f1'
396
+ ```
397
+
398
+ ## Pay
399
+
400
+ MoMo PAY APIs enable businesses to disburse payments to other consumers or businesses. This can be used in the context of salary payment, benefits disbursements and any other pay out. It can also be used to pay other business (suppliers).
401
+
402
+ PAY APIs utilize the Disbursement Subscription Keys.
403
+
404
+ ### Transfer
405
+
406
+ Make MoMo Transfer from Your Business to Other Business, Customers, Employees in the context of salary, bet winnings, Service Payments.
407
+
408
+ The MoMo Transfer API accommodates one MSISDN/ALIAS per request, and not bulk.
409
+
410
+ **Business to Business Payments** by using their Merchant ID:
411
+
412
+ ```json
413
+ {
414
+ "amount": "5900",
415
+ "currency": "EUR",
416
+ "externalId": "890695",
417
+ "payee": {
418
+ "partyIdType": "ALIAS",
419
+ "partyId": "678997678"
420
+ },
421
+ "payerMessage": "Salary Payment April 2023",
422
+ "payeeNote": "Salary Payment April 2023"
423
+ }
424
+ ```
425
+
426
+ **Business to Customer/Employee Payments** by using their MSISDN with country code:
427
+
428
+ ```json
429
+ {
430
+ "amount": "5900",
431
+ "currency": "EUR",
432
+ "externalId": "890695",
433
+ "payee": {
434
+ "partyIdType": "MSISDN",
435
+ "partyId": "2567524561"
436
+ },
437
+ "payerMessage": "Salary Payment April 2023",
438
+ "payeeNote": "Salary Payment April 2023"
439
+ }
440
+ ```
441
+
442
+ **Endpoint:** `POST {{base_url}}/disbursement/v1_0/transfer`
443
+
444
+ **Request Body**
445
+ - **amount** (string): The amount to be transferred
446
+ - **currency** (string): The currency in which the amount is specified
447
+ - **externalId** (string): An ID to uniquely identify the transfer
448
+ - **payee** (object): Information about the payee
449
+ - **partyIdType** (string): Type of ID for the payee
450
+ - **partyId** (string): ID of the payee
451
+ - **payerMessage** (string): Message from the payer
452
+ - **payeeNote** (string): Note for the payee
453
+
454
+ **Request Body Example**
455
+
456
+ ```json
457
+ {
458
+ "amount": "5900",
459
+ "currency": "EUR",
460
+ "externalId": "890695",
461
+ "payee": {
462
+ "partyIdType": "MSISDN",
463
+ "partyId": "{{MSISDN}}"
464
+ },
465
+ "payerMessage": "Salary Payment April 2023",
466
+ "payeeNote": "Salary Payment April 2023"
467
+ }
468
+ ```
469
+
470
+ **Authorization**
471
+ - Bearer Token: `{{Access_Token}}`
472
+
473
+ **Example Request**
474
+
475
+ ```bash
476
+ curl --location 'https://sandbox.momodeveloper.mtn.com/disbursement/v1_0/transfer' \
477
+ --header 'X-Callback-Url: https://webhook.site/mywebhooksandbox' \
478
+ --header 'X-Reference-Id: 1077fbc2-9253-437f-883c-30e248bc635d' \
479
+ --header 'X-Target-Environment: sandbox' \
480
+ --header 'Content-Type: application/json' \
481
+ --header 'Ocp-Apim-Subscription-Key: 0fd9826a39aa468fb311d23fc08a55fc' \
482
+ --data '{
483
+ "amount": "5900",
484
+ "currency": "EUR",
485
+ "externalId": "890695",
486
+ "payee": {
487
+ "partyIdType": "MSISDN",
488
+ "partyId": "56733123455"
489
+ },
490
+ "payerMessage": "Salary Payment April 2023",
491
+ "payeeNote": "Salary Payment April 2023"
492
+ }'
493
+ ```
494
+
495
+ ### Transfer Status
496
+
497
+ **Endpoint:** `GET https://sandbox.momodeveloper.mtn.com/disbursement/v1_0/transfer/:Request_ID_Transfer`
498
+
499
+ This endpoint makes an HTTP GET request to retrieve information about a specific transfer identified by the provided Request ID.
500
+
501
+ **Response Body**
502
+ The response includes the following fields in JSON format:
503
+ - **amount**: The amount of the transfer
504
+ - **currency**: The currency used for the transfer
505
+ - **financialTransactionId**: The financial transaction ID associated with the transfer
506
+ - **externalId**: The external ID associated with the transfer
507
+ - **payee**: An object containing information about the payee, including party ID type and party ID
508
+ - **payerMessage**: The message from the payer associated with the transfer
509
+ - **payeeNote**: The note from the payee associated with the transfer
510
+ - **status**: The status of the transfer
511
+
512
+ **Authorization**
513
+ - Bearer Token: `{{Access_Token}}`
514
+
515
+ **Path Variables**
516
+ - **Request_ID_Transfer**: `{{Request_ID_Transfer}}`
517
+
518
+ **Example Request**
519
+
520
+ ```bash
521
+ curl --location 'https://sandbox.momodeveloper.mtn.com/disbursement/v1_0/transfer/1077fbc2-9253-437f-883c-30e248bc635d' \
522
+ --header 'X-Target-Environment: sandbox' \
523
+ --header 'Ocp-Apim-Subscription-Key: 0fd9826a39aa468fb311d23fc08a55fc'
524
+ ```
525
+
526
+ ## Account Validation
527
+
528
+ To avoid Debiting Or Transferring invalid / Wrong MoMo Accounts, good practice to validate them and confirm ownership. Get Names and KYC and Account Status (Active- True, Inactive-False).
529
+
530
+ ### Validation No Consent
531
+
532
+ API Folder containing APIs that can be validated without requiring customer consent.
533
+
534
+ #### Check Account Holder
535
+
536
+ Check Account Holder API - This API provides information on whether an account has a MoMo wallet or not. It returns either true or false.
537
+
538
+ **Endpoint:** `GET {{base_url}}/disbursement/v1_0/accountholder/msisdn/:MSISDN/active`
539
+
540
+ Retrieve Active Account Holder by MSISDN - This endpoint makes an HTTP GET request to retrieve the active account holder by MSISDN.
541
+
542
+ **URL Parameters**
543
+ - **MSISDN** (required, string): The mobile number for which the active account holder information is to be retrieved
544
+
545
+ **Response**
546
+ The response will be in JSON format and will contain the following key:
547
+ - **result** (boolean): Indicates whether the account holder is active or not
548
+
549
+ **Example response:**
550
+
551
+ ```json
552
+ {
553
+ "result": true
554
+ }
555
+ ```
556
+
557
+ **Authorization**
558
+ - Bearer Token: `{{Access_Token}}`
559
+
560
+ **Path Variables**
561
+ - **MSISDN**: `{{MSISDN}}`
562
+
563
+ #### Get Basic Info (MSISDN)
564
+
565
+ Get Basic Info (MSISDN) API - This API retrieves the first and last names of the MoMo wallet owner. If the wallet does not exist, it returns an error message stating "Not Found".
566
+
567
+ **Endpoint:** `GET {{base_url}}/remittance/v1_0/accountholder/msisdn/:MSISDN/basicuserinfo`
568
+
569
+ This endpoint retrieves basic user information for the account holder associated with the provided MSISDN (Mobile Station International Subscriber Directory Number).
570
+
571
+ **Headers**
572
+ - **Authorization** (Bearer Token): The request is authenticated using a Bearer Token
573
+ - **X-Target-Environment**: The identifier on the API Request specifying which country the request is to be processed for production
574
+ - **Ocp-Apim-Subscription-Key**: Subscription key which provides access to the APIM
575
+
576
+ **Path Parameter**
577
+ - **MSISDN**: The Mobile Station International Subscriber Directory Number for which the basic user information is to be retrieved
578
+
579
+ **Response**
580
+ Upon successful execution, the response will have a status code of 200 and a JSON object with the following fields:
581
+
582
+ ```json
583
+ {
584
+ "sub": "",
585
+ "name": "",
586
+ "given_name": "",
587
+ "family_name": "",
588
+ "birthdate": "",
589
+ "locale": "",
590
+ "gender": "",
591
+ "updated_at": 0
592
+ }
593
+ ```
594
+
595
+ **Authorization**
596
+ - Bearer Token: `{{Access_Token}}`
597
+
598
+ **Path Variables**
599
+ - **MSISDN**: `{{MSISDN}}`
600
+
601
+ #### Get Basic Info (Alias)
602
+
603
+ Get Basic Info (Alias) API - This API retrieves the first and last names of the MoMo wallet owner using an Alias (Business Merchant ID). If the wallet does not exist, it returns an error message stating "Not Found".
604
+
605
+ **Endpoint:** `GET {{base_url}}/disbursement/v1_0/accountholder/Alias/:MSISDN/basicuserinfo`
606
+
607
+ **Authorization**
608
+ - Bearer Token: `{{Access_Token}}`
609
+
610
+ **Path Variables**
611
+ - **MSISDN**: `{{MSISDN}}`
612
+
613
+ ### Detailed KYC - With Consent
614
+
615
+ KYC With Consent APIs, Used to obtain Consent from the Customer to access their KYC information, like National ID, Date of Birth.
616
+
617
+ #### bc-authorize
618
+
619
+ **Endpoint:** `POST {{base_url}}/disbursement/v1_0/bc-authorize`
620
+
621
+ This endpoint allows you to make an HTTP POST request to authorize a disbursement.
622
+
623
+ **Request Body**
624
+ The request should include the following parameters in x-www-form-urlencoded format:
625
+ - **scope**: all_info
626
+ - **login_hint**: ID:563667/MSISDN
627
+ - **access_type**: offline
628
+
629
+ **Authorization**
630
+ - Bearer Token: `{{Access_Token}}`
631
+
632
+ **Example Request**
633
+
634
+ ```bash
635
+ curl --location 'https://sandbox.momodeveloper.mtn.com/disbursement/v1_0/bc-authorize' \
636
+ --header 'X-Target-Environment: sandbox' \
637
+ --header 'X-Callback-Url: https://webhook.site/mywebhooksandbox' \
638
+ --header 'Content-Type: application/x-www-form-urlencoded' \
639
+ --header 'Ocp-Apim-Subscription-Key: 0fd9826a39aa468fb311d23fc08a55fc' \
640
+ --data-urlencode 'scope=all_info' \
641
+ --data-urlencode 'login_hint=ID:563667/MSISDN' \
642
+ --data-urlencode 'access_type=offline'
643
+ ```
644
+
645
+ #### Generate Oauth2 Token
646
+
647
+ **Endpoint:** `POST {{base_url}}/disbursement/oauth2/token/`
648
+
649
+ This endpoint allows the client to obtain an OAuth2 token for disbursement.
650
+
651
+ **Response JSON Schema**
652
+
653
+ ```json
654
+ {
655
+ "type": "object",
656
+ "properties": {
657
+ "access_token": {
658
+ "type": "string"
659
+ },
660
+ "token_type": {
661
+ "type": "string"
662
+ },
663
+ "expires_in": {
664
+ "type": "integer"
665
+ },
666
+ "scope": {
667
+ "type": "string"
668
+ }
669
+ }
670
+ }
671
+ ```
672
+
673
+ **Authorization**
674
+ - Basic Auth
675
+ - Username: `{{api_user}}`
676
+ - Password: `{{api_key}}`
677
+
678
+ **Request Body**
679
+ - **grant_type**: urn:openid:params:grant-type:ciba
680
+ - **auth_req_id**: `{{Consent_Req_Id}}`
681
+
682
+ **Example Request**
683
+
684
+ ```bash
685
+ curl --location 'https://sandbox.momodeveloper.mtn.com/disbursement/oauth2/token/' \
686
+ --header 'X-Target-Environment: sandbox' \
687
+ --header 'Content-Type: application/x-www-form-urlencoded' \
688
+ --header 'Ocp-Apim-Subscription-Key: 0fd9826a39aa468fb311d23fc08a55fc' \
689
+ --data-urlencode 'grant_type=urn:openid:params:grant-type:ciba' \
690
+ --data-urlencode 'auth_req_id=010001b2-5c84-4dd1-9eaf-56d5668dabd3'
691
+ ```
692
+
693
+ #### Get UserInfo
694
+
695
+ **Endpoint:** `GET {{base_url}}/disbursement/oauth2/v1_0/userinfo`
696
+
697
+ This endpoint is used to retrieve user information via an HTTP GET request.
698
+
699
+ **Authorization**
700
+ - Bearer Token: `{{Oauth2_Token}}`
701
+
702
+ **Example Request**
703
+
704
+ ```bash
705
+ curl --location --globoff '{{base_url}}/disbursement/oauth2/v1_0/userinfo' \
706
+ --header 'X-Target-Environment: {{Target_Environment}}' \
707
+ --header 'Ocp-Apim-Subscription-Key: {{Disbursement_Subscription-Key}}'
708
+ ```
709
+
710
+ ## Distribute
711
+
712
+ MoMo Open APIs enable business to distribute MTN and MoMo service and facilitate Cash In, Cash out and Airtime sale and get commission from MTN and MoMo.
713
+
714
+ Folder contains Cash In APIs and Cash Out.
715
+
716
+ ### Cash IN
717
+
718
+ #### Deposit Request
719
+
720
+ **Endpoint:** `POST https://sandbox.momodeveloper.mtn.com/disbursement/v1_0/deposit`
721
+
722
+ Deposit Disbursement - This endpoint allows you to initiate a deposit disbursement.
723
+
724
+ **Request Body**
725
+ - **amount** (string): The amount to be deposited
726
+ - **currency** (string): The currency of the amount
727
+ - **externalId** (string): Your unique identifier for this transaction
728
+ - **payee.partyIdType** (string): The type of the party ID
729
+ - **payee.partyId** (string): The ID of the party to receive the deposit
730
+ - **payerMessage** (string): Message from the payer
731
+ - **payeeNote** (string): Note to the payee
732
+
733
+ **Request Body Example**
734
+
735
+ ```json
736
+ {
737
+ "amount": "2679",
738
+ "currency": "EUR",
739
+ "externalId": "004504",
740
+ "payee": {
741
+ "partyIdType": "MSISDN",
742
+ "partyId": "{{MSISDN}}"
743
+ },
744
+ "payerMessage": "MoMo CASH IN Thank You",
745
+ "payeeNote": "MoMo CASH IN Thank You"
746
+ }
747
+ ```
748
+
749
+ **Authorization**
750
+ - Bearer Token: `{{Access_Token}}`
751
+
752
+ **Example Request**
753
+
754
+ ```bash
755
+ curl --location 'https://sandbox.momodeveloper.mtn.com/disbursement/v1_0/deposit' \
756
+ --header 'X-Callback-Url: https://webhook.site/mywebhooksandbox' \
757
+ --header 'X-Reference-Id;' \
758
+ --header 'X-Target-Environment: sandbox' \
759
+ --header 'Content-Type: application/json' \
760
+ --header 'Ocp-Apim-Subscription-Key: 0fd9826a39aa468fb311d23fc08a55fc' \
761
+ --data '{
762
+ "amount": "2679",
763
+ "currency": "EUR",
764
+ "externalId": "004504",
765
+ "payee": {
766
+ "partyIdType": "MSISDN",
767
+ "partyId": "56733123455"
768
+ },
769
+ "payerMessage": "MoMo CASH IN Thank You",
770
+ "payeeNote": "MoMo CASH IN Thank You"
771
+ }'
772
+ ```
773
+
774
+ #### Deposit Request Status
775
+
776
+ **Endpoint:** `GET {{base_url}}/disbursement/v1_0/deposit/:Request_ID_CashIn`
777
+
778
+ This API endpoint makes an HTTP GET request to retrieve the details of a specific deposit identified by the Request_ID_CashIn in the URL.
779
+
780
+ **Response**
781
+
782
+ ```json
783
+ {
784
+ "externalId":"",
785
+ "amount":"",
786
+ "currency":"",
787
+ "payee":{
788
+ "partyIdType":"",
789
+ "partyId":""
790
+ },
791
+ "payerMessage":"",
792
+ "payeeNote":"",
793
+ "status":""
794
+ }
795
+ ```
796
+
797
+ **Authorization**
798
+ - Bearer Token: `{{Access_Token}}`
799
+
800
+ **Path Variables**
801
+ - **Request_ID_CashIn**: `{{Request_ID_CashIn}}`
802
+
803
+ **Example Request**
804
+
805
+ ```bash
806
+ curl --location 'https://sandbox.momodeveloper.mtn.com/disbursement/v1_0/deposit/:Request_ID_CashIn' \
807
+ --header 'X-Target-Environment: sandbox' \
808
+ --header 'Ocp-Apim-Subscription-Key: 0fd9826a39aa468fb311d23fc08a55fc'
809
+ ```
810
+
811
+ ### Cash OUT
812
+
813
+ #### Request To Withdraw
814
+
815
+ **Endpoint:** `POST {{base_url}}/collection/v1_0/requesttowithdraw`
816
+
817
+ This endpoint allows the user to initiate a request to withdraw funds.
818
+
819
+ **Request Body**
820
+ - **amount** (string, required): The amount to be withdrawn
821
+ - **currency** (string, required): The currency in which the withdrawal is to be made
822
+ - **externalId** (string, required): The unique identifier for the withdrawal request
823
+ - **payer** (object, required):
824
+ - **partyIdType** (string, required): The type of party ID for the payer
825
+ - **partyId** (string, required): The ID of the payer
826
+ - **payerMessage** (string, optional): A message from the payer
827
+ - **payeeNote** (string, optional): A note for the payee
828
+
829
+ **Request Body Example**
830
+
831
+ ```json
832
+ {
833
+ "amount": "3536636",
834
+ "currency": "EUR",
835
+ "externalId": "45757757",
836
+ "payer": {
837
+ "partyIdType": "MSISDN",
838
+ "partyId": "{{MSISDN}}"
839
+ },
840
+ "payerMessage": "MoMo Cash Out Thank You",
841
+ "payeeNote": "MoMo Cash Out Thank You "
842
+ }
843
+ ```
844
+
845
+ **Authorization**
846
+ - Bearer Token: `{{Access_Token}}`
847
+
848
+ **Example Request**
849
+
850
+ ```bash
851
+ curl --location 'https://sandbox.momodeveloper.mtn.com/collection/v1_0/requesttowithdraw' \
852
+ --header 'X-Callback-Url: https://webhook.site/mywebhooksandbox' \
853
+ --header 'X-Reference-Id;' \
854
+ --header 'X-Target-Environment: sandbox' \
855
+ --header 'Content-Type: application/json' \
856
+ --header 'Ocp-Apim-Subscription-Key: bca0c92d326a46cd885f443d51b859f1' \
857
+ --data '{
858
+ "amount": "3536636",
859
+ "currency": "EUR",
860
+ "externalId": "45757757",
861
+ "payer": {
862
+ "partyIdType": "MSISDN",
863
+ "partyId": "56733123455"
864
+ },
865
+ "payerMessage": "MoMo Cash Out Thank You",
866
+ "payeeNote": "MoMo Cash Out Thank You "
867
+ }'
868
+ ```
869
+
870
+ #### Request To Withdraw Status
871
+
872
+ **Endpoint:** `GET {{base_url}}/collection/v1_0/requesttowithdraw/:Request_ID_CashOut`
873
+
874
+ This endpoint makes an HTTP GET request to retrieve information about a specific cash withdrawal request identified by the provided Request_ID_CashOut.
875
+
876
+ **Response**
877
+ Upon a successful execution, the endpoint returns a JSON object with the following fields:
878
+ - **financialTransactionId** (string): The ID of the financial transaction
879
+ - **externalId** (string): The external ID associated with the transaction
880
+ - **amount** (string): The amount of the withdrawal
881
+ - **currency** (string): The currency in which the withdrawal is made
882
+ - **payer** (object): An object containing information about the payer, including partyIdType and partyId
883
+ - **payerMessage** (string): A message from the payer
884
+ - **payeeNote** (string): A note from the payee
885
+ - **status** (string): The status of the withdrawal request
886
+
887
+ **Authorization**
888
+ - Bearer Token: `{{Access_Token}}`
889
+
890
+ **Path Variables**
891
+ - **Request_ID_CashOut**: `{{Request_ID_CashOut}}`
892
+
893
+ **Example Request**
894
+
895
+ ```bash
896
+ curl --location 'https://sandbox.momodeveloper.mtn.com/collection/v1_0/requesttowithdraw/:Request_ID_CashOut' \
897
+ --header 'X-Target-Environment: sandbox' \
898
+ --header 'Ocp-Apim-Subscription-Key: bca0c92d326a46cd885f443d51b859f1'
899
+ ```
900
+
901
+ ## Invoice
902
+
903
+ A Business may use this in order to create an invoice that can be paid by an intended payer via any channel at a later stage.
904
+
905
+ ### Create Invoice
906
+
907
+ **Endpoint:** `POST {{base_url}}collection/v2_0/invoice`
908
+
909
+ This endpoint allows you to create a new invoice. The customer will receive an SMS with the Invoice ID.
910
+
911
+ The Payments can be made via MoMo USSD or MoMo APP.
912
+
913
+ **Request Body**
914
+ - **externalId** (string): The external identifier for the invoice
915
+ - **amount** (string): The amount of the invoice
916
+ - **currency** (string): The currency in which the invoice amount is specified
917
+ - **validityDuration** (string): The duration for which the invoice is valid
918
+ - **intendedPayer** (object): An object containing the party ID type and party ID of the intended payer
919
+ - **partyIdType** ("MSISDN/ALIAS"): The type of party ID for the intended payer
920
+ - **partyId** (MSISDN ID): The party ID of the intended payer
921
+ - **payee** (object): An object containing the party ID type and party ID of the payee
922
+ - **partyIdType** (MSISDN): The type of party ID for the payee
923
+ - **partyId** (string): The party ID of the payee
924
+ - **description** (string): Description of the invoice
925
+
926
+ **Request Body Example**
927
+
928
+ ```json
929
+ {
930
+ "externalId": "005060",
931
+ "amount": "5000",
932
+ "currency": "EUR",
933
+ "validityDuration": "360",
934
+ "intendedPayer": {
935
+ "partyIdType": "MSISDN",
936
+ "partyId": "56784939"
937
+ },
938
+ "payee": {
939
+ "partyIdType": "MSISDN",
940
+ "partyId": "46733123450"
941
+ },
942
+ "description": "Invoice SandBox"
943
+ }
944
+ ```
945
+
946
+ **Authorization**
947
+ - Bearer Token: `{{Access_Token}}`
948
+
949
+ ### Invoice Status
950
+
951
+ **Endpoint:** `GET {{base_url}}/collection/v2_0/invoice/:Request_ID_Invoice`
952
+
953
+ **Authorization**
954
+ - Bearer Token: `{{Access_Token}}`
955
+
956
+ **Path Variables**
957
+ - **Request_ID_Invoice**: `{{Request_ID_Invoice}}`
958
+
959
+ ### Delete Invoice
960
+
961
+ **Endpoint:** `DELETE {{base_url}}/collection/v2_0/invoice/:Request_ID_Invoice`
962
+
963
+ **Authorization**
964
+ - Bearer Token: `{{Access_Token}}`
965
+
966
+ **Path Variables**
967
+ - **Request_ID_Invoice**: `{{Request_ID_Invoice}}`
968
+
969
+ **Request Body Example**
970
+
971
+ ```json
972
+ {
973
+ "externalId": "57r75677"
974
+ }
975
+ ```
976
+
977
+ ## Pre Approval
978
+
979
+ Pre Approval API is used to get consent from the customer to be Debited for a specified period of time. The Consent is approved by the customer providing the correct PIN. The Consent can be cancelled anytime by the customer.
980
+
981
+ ### Request Preapproval
982
+
983
+ **Endpoint:** `POST {{base_url}}/collection/v2_0/preapproval`
984
+
985
+ Create Preapproval - This endpoint allows you to create a preapproval for a collection.
986
+
987
+ **Request Body**
988
+ - **payer** (object, required): Information about the payer including partyIdType and partyId
989
+ - **payerCurrency** (string, required): The currency of the payer
990
+ - **payerMessage** (string, required): A message from the payer
991
+ - **validityTime** (string, required): The validity time of the preapproval
992
+
993
+ **Request Body Example**
994
+
995
+ ```json
996
+ {
997
+ "payer": {
998
+ "partyIdType": "MSISDN",
999
+ "partyId": "{{MSISDN}}"
1000
+ },
1001
+ "payerCurrency": "EUR",
1002
+ "payerMessage": "HI MoMo API",
1003
+ "validityTime": "300"
1004
+ }
1005
+ ```
1006
+
1007
+ **Authorization**
1008
+ - Bearer Token: `{{Access_Token}}`
1009
+
1010
+ **Example Request**
1011
+
1012
+ ```bash
1013
+ curl --location --globoff '{{base_url}}/collection/v2_0/preapproval' \
1014
+ --header 'X-Callback-Url: {{CallbackURL}}' \
1015
+ --header 'X-Reference-Id: {{Request_ID_Preapproval}}' \
1016
+ --header 'X-Target-Environment: {{Target_Environment}}' \
1017
+ --header 'Content-Type: application/json' \
1018
+ --header 'Ocp-Apim-Subscription-Key: {{Collection_Subscription-Key}}' \
1019
+ --data '{
1020
+ "payer": {
1021
+ "partyIdType": "MSISDN",
1022
+ "partyId": "{{MSISDN}}"
1023
+ },
1024
+ "payerCurrency": "EUR",
1025
+ "payerMessage": "HI MoMo API",
1026
+ "validityTime": "300"
1027
+ }'
1028
+ ```
1029
+
1030
+ ### Get PreApproval Status
1031
+
1032
+ **Endpoint:** `GET {{base_url}}/collection/v2_0/preapproval/:Request_ID_Preapproval`
1033
+
1034
+ This API endpoint retrieves the details of a specific preapproval by providing the preapproval request ID in the URL.
1035
+
1036
+ **Response**
1037
+ Upon a successful execution (Status: 200), the response will be in JSON format with the following fields:
1038
+ - **payer**
1039
+ - **partyIdType**
1040
+ - **partyId**
1041
+ - **payerCurrency**
1042
+ - **payerMessage**
1043
+ - **expirationDateTime**
1044
+ - **status**
1045
+
1046
+ **Authorization**
1047
+ - Bearer Token: `{{Access_Token}}`
1048
+
1049
+ **Path Variables**
1050
+ - **Request_ID_Preapproval**: `{{Request_ID_Preapproval}}`
1051
+
1052
+ **Example Request**
1053
+
1054
+ ```bash
1055
+ curl --location --globoff '{{base_url}}/collection/v2_0/preapproval/{{Request_ID_Preapproval}}' \
1056
+ --header 'X-Target-Environment: {{Target_Environment}}' \
1057
+ --header 'Ocp-Apim-Subscription-Key: {{Collection_Subscription-Key}}'
1058
+ ```
1059
+
1060
+ ## Remittance
1061
+
1062
+ The Remittance API empowers businesses by enabling users to transmit or receive funds from overseas straight into their Mobile Money accounts, all in local currency.
1063
+
1064
+ ### Transfer
1065
+
1066
+ **Endpoint:** `POST {{base_url}}/remittance/v1_0/transfer`
1067
+
1068
+ **Headers**
1069
+ - **X-Reference-Id**: The UUID Version 4 Format String, labeled as Request_ID_Transfer, is used to uniquely identify a transaction and retrieve the status
1070
+ - **Authorization**: Bearer Token, the Access_token, is automatically generated in the pre-request script of the collection
1071
+ - **X-Target-Environment**: Specifies the country the request is to be processed for production, using sandbox during testing
1072
+ - **Content-Type**: application/json
1073
+ - **Ocp-Apim-Subscription-Key**: Subscription key providing access to the APIM
1074
+ - **X-Callback-Url**: Secure HTTPS URL for receiving responses
1075
+
1076
+ **Request Body**
1077
+ - **Amount** (string): Amount to be transferred to the MoMo Subscriber
1078
+ - **Currency** (string): Varies depending on the country, with Sandbox using EUR
1079
+ - **ExternalId** (string): String used for reconciliation between Partner Platform and MoMo Platform
1080
+ - **PartyIdType** (string): Supported types are MSISDN and ALIAS
1081
+ - **PartyId** (string): Value of the MSISDN Number or Merchant ID
1082
+ - **PayerMessage** (string): Notification sent to the Sender
1083
+ - **PayeeNote** (string): Notification sent to the receiver
1084
+
1085
+ **Request Body Example**
1086
+
1087
+ ```json
1088
+ {
1089
+ "amount": "980000",
1090
+ "currency": "EUR",
1091
+ "externalId": "9087699",
1092
+ "payee": {
1093
+ "partyIdType": "MSISDN",
1094
+ "partyId": "{{MSISDN}}"
1095
+ },
1096
+ "payerMessage": "remittance amount",
1097
+ "payeeNote": "remittance amount"
1098
+ }
1099
+ ```
1100
+
1101
+ **Authorization**
1102
+ - Bearer Token: `{{Access_Token}}`
1103
+
1104
+ **Example Request**
1105
+
1106
+ ```bash
1107
+ curl --location --globoff '{{base_url}}/remittance/v1_0/transfer' \
1108
+ --header 'X-Callback-Url: {{CallbackURL}}' \
1109
+ --header 'X-Reference-Id: {{Request_ID_Remit_Transfer}}' \
1110
+ --header 'X-Target-Environment: {{Target_Environment}}' \
1111
+ --header 'Content-Type: application/json' \
1112
+ --header 'Ocp-Apim-Subscription-Key: {{Remittance_Subscription-Key}}' \
1113
+ --data '{
1114
+ "amount": "980000",
1115
+ "currency": "EUR",
1116
+ "externalId": "9087699",
1117
+ "payee": {
1118
+ "partyIdType": "MSISDN",
1119
+ "partyId": "{{MSISDN}}"
1120
+ },
1121
+ "payerMessage": "remittance amount",
1122
+ "payeeNote": "remittance amount"
1123
+ }'
1124
+ ```
1125
+
1126
+ ### Transfer Status
1127
+
1128
+ **Endpoint:** `GET https://sandbox.momodeveloper.mtn.com/remittance/v1_0/transfer/:Request_ID_Remit_Transfer`
1129
+
1130
+ This HTTP GET request is used to retrieve information about a specific remittance transfer identified by the Request ID.
1131
+
1132
+ **Response**
1133
+ Upon a successful execution, the API returns a JSON response with the following fields:
1134
+ - **amount**: The amount of the remittance transfer
1135
+ - **currency**: The currency of the remittance transfer
1136
+ - **financialTransactionId**: The financial transaction ID associated with the transfer
1137
+ - **externalId**: The external ID associated with the transfer
1138
+ - **payee**: An object containing information about the payee, including party ID type and party ID
1139
+ - **payerMessage**: The message from the payer associated with the transfer
1140
+ - **payeeNote**: The note from the payee associated with the transfer
1141
+ - **status**: The status of the remittance transfer
1142
+
1143
+ **Authorization**
1144
+ - Bearer Token: `{{Access_Token}}`
1145
+
1146
+ **Path Variables**
1147
+ - **Request_ID_Remit_Transfer**: `{{Request_ID_Remit_Transfer}}`
1148
+
1149
+ **Example Request**
1150
+
1151
+ ```bash
1152
+ curl --location --globoff 'https://sandbox.momodeveloper.mtn.com/remittance/v1_0/transfer/{{Request_ID_Remit_Transfer}}' \
1153
+ --header 'X-Target-Environment: {{Target_Environment}}' \
1154
+ --header 'Ocp-Apim-Subscription-Key: {{Remittance_Subscription-Key}}'
1155
+ ```
1156
+
1157
+ ## CashTransfer
1158
+
1159
+ CashTransfer API is the enriched Remittance API, designed to facilitate the efficient capture of Know Your Customer (KYC) details for both the payer and payee involved in fund transfers.
1160
+
1161
+ ### Field Descriptions:
1162
+
1163
+ - **amount**: This is the sum that the recipient will receive following currency conversion
1164
+ - **currency**: Specifies the type of currency in which the recipient will receive the amount (ISO 4217 Currency Codes)
1165
+ - **payee**: Contains information about the beneficiary
1166
+ - **partyId**: A unique identifier for the recipient, which could be a phone number, email, or a designated party code
1167
+ - **partyIdType**: The kind of identifier being used, such as MSISDN, EMAIL, or PARTY_CODE
1168
+ - **externalId**: This is the transaction ID used for business applications and is useful for reconciliation purposes
1169
+ - **originatingCountry**: The country from which the funds are being sent
1170
+ - **originalAmount**: The initial sum to be received before any currency conversion takes place
1171
+ - **originalCurrency**: The currency in which the original sum is denominated (ISO 4217 Currency Codes)
1172
+ - **payerMessage**: A message that will be delivered to the sender
1173
+ - **payeeNote**: A note that will be delivered to the beneficiary
1174
+ - **payerIdentificationType**: The type of identification used for the sender
1175
+
1176
+ ### Payer Identification Types:
1177
+ - **CPFA**: CPF account number
1178
+ - **SRSA**: SRS account number
1179
+ - **NRIN**: National registration identification number
1180
+ - **OTHR**: Other
1181
+ - **DRLC**: Drivers license number
1182
+ - **PASS**: Passport number
1183
+ - **SOCS**: Social security number
1184
+ - **AREG**: Alien registration number
1185
+ - **IDCD**: Identity card number
1186
+ - **EMID**: Employer identification number
1187
+
1188
+ - **payerIdentificationNumber**: The identification number that corresponds to the specified identification type
1189
+ - **payerIdentity**: The MSISDN utilized by the sender in the country where the remittance originates. Format: (ID:6873248686/MSISDN)
1190
+ - **payerFirstName** / **payerSurName**: The given name and surname of the sender
1191
+ - **payerLanguageCode**: The linguistic code corresponding to the sender's nation (ISO_639-1)
1192
+ - **payerEmail**: The sender's electronic mail address (Validated)
1193
+ - **payerMsisdn**: The phone number of the sender
1194
+ - **payerGender**: The sex of the sender (MALE, FEMALE, OTHER)
1195
+
1196
+ ### cashtransfer
1197
+
1198
+ **Endpoint:** `POST {{base_url}}/remittance/v2_0/cashtransfer`
1199
+
1200
+ **Request Headers**
1201
+ - **X-Reference-Id**: `{{Request_ID_Cash_Transfer}}`
1202
+ - **X-Target-Environment**: `{{Target_Environment}}`
1203
+ - **Content-Type**: application/json
1204
+ - **Ocp-Apim-Subscription-Key**: `{{Remittance_Subscription-Key}}`
1205
+
1206
+ **Request Body Example**
1207
+
1208
+ ```json
1209
+ {
1210
+ "amount": "208",
1211
+ "currency": "EUR",
1212
+ "payee": {
1213
+ "partyIdType": "MSISDN",
1214
+ "partyId": "{{MSISDN}}"
1215
+ },
1216
+ "externalId": "XTR1718714047",
1217
+ "orginatingCountry": "SO",
1218
+ "originalAmount": "1",
1219
+ "originalCurrency": "USD",
1220
+ "payerMessage": "From SO to MTN UG Payer",
1221
+ "payeeNote": "Test",
1222
+ "payerIdentificationType": "PASS",
1223
+ "payerIdentificationNumber": "UG0002",
1224
+ "payerIdentity": "ID:256701081899/MSISDN",
1225
+ "payerFirstName": "Abdirazak",
1226
+ "payerSurName": "Ibrahim",
1227
+ "payerLanguageCode": "en",
1228
+ "payerEmail": "abdirazak.ibrahim@tt.com",
1229
+ "payerMsisdn": "252654249184",
1230
+ "payerGender": "MALE"
1231
+ }
1232
+ ```
1233
+
1234
+ **Authorization**
1235
+ - Bearer Token: `{{Access_Token}}`
1236
+
1237
+ **Example Request**
1238
+
1239
+ ```bash
1240
+ curl --location --globoff '{{base_url}}/remittance/v2_0/cashtransfer' \
1241
+ --header 'X-Reference-Id: {{Request_ID_Cash_Transfer}}' \
1242
+ --header 'X-Target-Environment: {{Target_Environment}}' \
1243
+ --header 'Content-Type: application/json' \
1244
+ --header 'Ocp-Apim-Subscription-Key: {{Remittance_Subscription-Key}}' \
1245
+ --data-raw '{
1246
+ "amount": "208",
1247
+ "currency": "EUR",
1248
+ "payee": {
1249
+ "partyIdType": "MSISDN",
1250
+ "partyId": "{{MSISDN}}"
1251
+ },
1252
+ "externalId": "XTR1718714047",
1253
+ "orginatingCountry": "SO",
1254
+ "originalAmount": "1",
1255
+ "originalCurrency": "USD",
1256
+ "payerMessage": "From SO to MTN UG Payer",
1257
+ "payeeNote": "Test",
1258
+ "payerIdentificationType": "PASS",
1259
+ "payerIdentificationNumber": "UG0002",
1260
+ "payerIdentity": "ID:256701081899/MSISDN",
1261
+ "payerFirstName": "Abdirazak",
1262
+ "payerSurName": "Ibrahim",
1263
+ "payerLanguageCode": "en",
1264
+ "payerEmail": "abdirazak.ibrahim@tt.com",
1265
+ "payerMsisdn": "252654249184",
1266
+ "payerGender": "MALE"
1267
+ }'
1268
+ ```
1269
+
1270
+ ### cashtransfer status
1271
+
1272
+ **Endpoint:** `GET {{base_url}}/remittance/v2_0/cashtransfer/:Request_ID_Cash_Transfer`
1273
+
1274
+ **Request Headers**
1275
+ - **X-Target-Environment**: `{{Target_Environment}}`
1276
+ - **Ocp-Apim-Subscription-Key**: `{{Remittance_Subscription-Key}}`
1277
+
1278
+ **Path Variables**
1279
+ - **Request_ID_Cash_Transfer**: `{{Request_ID_Cash_Transfer}}`
1280
+
1281
+ **Response Example**
1282
+
1283
+ ```json
1284
+ {
1285
+ "financialTransactionId": "179403634",
1286
+ "externalId": "XTR1718714047",
1287
+ "payee": {
1288
+ "partyIdType": "MSISDN",
1289
+ "partyId": "56733123453"
1290
+ },
1291
+ "amount": "208",
1292
+ "currency": "EUR",
1293
+ "originalAmount": "1",
1294
+ "originalCurrency": "USD",
1295
+ "payerMessage": "From SO to MTN UG Payer",
1296
+ "payeeNote": "Test",
1297
+ "status": "SUCCESSFUL",
1298
+ "payerIdentificationType": "PASS",
1299
+ "payerIdentificationNumber": "UG0002",
1300
+ "payerIdentity": "ID:256701081899/MSISDN",
1301
+ "payerFirstName": "Abdirazak",
1302
+ "payerSurName": "Ibrahim",
1303
+ "payerLanguageCode": "en",
1304
+ "payerEmail": "abdirazak.ibrahim@tt.com",
1305
+ "payerMsisdn": "252654249184",
1306
+ "payerGender": "MALE"
1307
+ }
1308
+ ```
1309
+
1310
+ **Authorization**
1311
+ - Bearer Token: `{{Access_Token}}`
1312
+
1313
+ **Example Request**
1314
+
1315
+ ```bash
1316
+ curl --location --globoff '{{base_url}}/remittance/v2_0/cashtransfer/{{Request_ID_Cash_Transfer}}' \
1317
+ --header 'X-Target-Environment: {{Target_Environment}}' \
1318
+ --header 'Ocp-Apim-Subscription-Key: {{Remittance_Subscription-Key}}'
1319
+ ```
1320
+
1321
+ ## Account Balance
1322
+
1323
+ MoMo API for Account Balance to check your personal account standings. Remember, this balance isn't for the client, but specifically for the Merchant Account - the Account Owner.
1324
+
1325
+ ### Get Account Balance
1326
+
1327
+ **Endpoint:** `GET {{base_url}}/collection/v1_0/account/balance`
1328
+
1329
+ This endpoint makes an HTTP GET request to retrieve the account balance from the specified collection.
1330
+
1331
+ **Authorization**
1332
+ - Bearer Token: `{{Access_Token}}`
1333
+
1334
+ ## Get API User and API Key
1335
+
1336
+ **Endpoint:** `POST {{base_url}}/v1_0/apiuser`
1337
+
1338
+ This endpoint makes an HTTP POST request to create an API user at the specified URL.
1339
+
1340
+ **Request Body**
1341
+
1342
+ ```json
1343
+ {
1344
+ "providerCallbackHost": "{{CallbackHost}}"
1345
+ }
1346
+ ```
1347
+
1348
+ **Example Request**
1349
+
1350
+ ```bash
1351
+ curl --location --globoff '{{base_url}}/v1_0/apiuser' \
1352
+ --header 'X-Reference-Id: {{api_user}}' \
1353
+ --header 'Content-Type: application/json' \
1354
+ --header 'Ocp-Apim-Subscription-Key: {{Collection_Subscription-Key}}' \
1355
+ --data '{
1356
+ "providerCallbackHost": {{CallbackHost}}
1357
+ }'
1358
+ ```
knowledge_base/pesapal/pesapal_e-commerce-api-3.0/pesapal_e-commerce_api_3.0_auth_docs.md ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Authentication
2
+
3
+ ## Authentication Endpoint - `Post Request`
4
+
5
+ Pesapal's API Authentication requests are done via a POST request.
6
+
7
+ Your Pesapal merchant **consumer_key** and **consumer_secret** will be used to generate an access token.
8
+
9
+ This access token is valid for a maximum period of **5 minutes.** Use this token (sent as a Bearer Token) to access all other Pesapal API 3.0 endpoints.
10
+
11
+ The URL to our token generation API is either:
12
+
13
+ - **Sandbox/Demo URL:** https://cybqa.pesapal.com/pesapalv3/api/Auth/RequestToken
14
+ - **Production/Live URL:** https://pay.pesapal.com/v3/api/Auth/RequestToken
15
+
16
+ ## HTTP Request Headers
17
+
18
+ **Accept:** The response format, which is required for operations with a response body. **Content-Type:** The request format, which is required for operations with a request body.
19
+
20
+ | Parameter | Required | Description |
21
+ |-----------|----------|-------------|
22
+ | Accept | `Required` | Should be set to `application/json` |
23
+ | Content-Type | `Required` | Should be set to `application/json` |
24
+
25
+ ## Request Parameters
26
+
27
+ Your live/production **consumer_key** and **consumer_secret** will be sent to your merchant email on opening a business/merchant account.
28
+
29
+ Please click here to open a live/production business account.
30
+
31
+ Click here to download test credentials to use while connecting to our **demo/sandbox** API.
32
+
33
+ | Parameter | Type | Required | Description |
34
+ |-----------|------|----------|-------------|
35
+ | consumer_key | String | `Required` | The consumer_key parameter must be set to `merchant consumer_key` |
36
+ | consumer_secret | String | `Required` | The consumer_secret parameter must be set to `merchant consumer_secret.` |
37
+
38
+ ## Sample Request
39
+
40
+ ```json
41
+ {
42
+ "consumer_key": "xxxxx",
43
+ "consumer_secret": "xxxxxx"
44
+ }
45
+ ```
46
+
47
+ ## Response Parameters
48
+
49
+ | Name | Type | Description |
50
+ |------|------|-------------|
51
+ | token | String | The access token string as issued by the server. The access token issued will have permission to invoke all Pesapa API operations. |
52
+ | expiryDate | String | Date and time the token will expire. The access token usually expires after 5mins - `UTC` |
53
+ | error | Object | Error object |
54
+ | status | String | Response code |
55
+ | message | String | A brief description about the response received. |
56
+
57
+ ## Sample Response
58
+
59
+ ```json
60
+ {
61
+ "token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYa",
62
+ "expiryDate": "2021-08-26T12:29:30.5177702Z",
63
+ "error": null,
64
+ "status": "200",
65
+ "message": "Request processed successfully"
66
+ }
67
+ ```
knowledge_base/pesapal/pesapal_e-commerce-api-3.0/pesapal_e-commerce_api_3.0_e-commerce_intro.md ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Pesapal API 3.0
2
+
3
+ Welcome to **Pesapal API 3.0**. Getting started with Pesapal is quick and easy!
4
+
5
+ In this release, you will learn how to use our API endpoints to access Pesapal services. Our APIs are built on **REST (Representational State Transfer)** thus our data entities are represented as HTTP resources and are accessed using HTTP verbs **GET** and **POST**. Requests and responses are **JSON encoded**.
6
+
7
+ Find information about integrating your website with Pesapal. This documentation includes sample codes for each API we have.
8
+
9
+ **Postman URL**: https://documenter.getpostman.com/view/6715320/UyxepTv1
10
+
11
+ ## Base URLs
12
+
13
+ | Environment | URL |
14
+ |-------------|-----|
15
+ | Sandbox | https://cybqa.pesapal.com/pesapalv3 |
16
+ | Live | https://pay.pesapal.com/v3 |
17
+
18
+ ## Error Object
19
+
20
+ In case an error occurs during any API call, Pesapal will respond with a json string in the following format:
21
+
22
+ ```json
23
+ {
24
+ "error": {
25
+ "type": "error_type",
26
+ "code": "response_code",
27
+ "message": "Detailed error message goes here..."
28
+ }
29
+ }
30
+ ```
knowledge_base/pesapal/pesapal_e-commerce-api-3.0/pesapal_e-commerce_api_3.0_get_ipn_docs.md ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Get Registered IPNs
2
+
3
+ ## Get Registered IPNs Endpoint - `Get Request`
4
+
5
+ This endpoint allows you to fetch all registered IPN URLs for a particular Pesapal merchant account.
6
+
7
+ The URL to our GetIPNList API is either:
8
+
9
+ - **Sandbox/Demo URL:** https://cybqa.pesapal.com/pesapalv3/api/URLSetup/GetIpnList
10
+ - **Production/Live URL:** https://pay.pesapal.com/v3/api/URLSetup/GetIpnList
11
+
12
+ ## Authentication
13
+
14
+ **Bearer Token:** Use token generated during authentication.
15
+
16
+ ## Request
17
+
18
+ No payload is required.
19
+
20
+ ## Response Parameters
21
+
22
+ | Name | Type | Description |
23
+ |------|------|-------------|
24
+ | url | String | The notification url Pesapal with send a status alert to. |
25
+ | created_date | String | Date and time the IPN URL was registered `UTC` |
26
+ | ipn_id | String | A unique identifier that's liked to the IPN endpoint URL. `GUID` |
27
+ | error | Integer | |
28
+ | status | String | Response code. |
29
+
30
+ ## Sample Response
31
+
32
+ ```json
33
+ [
34
+ {
35
+ "url": "https://www.myapplication.com/ipn",
36
+ "created_date": "2022-03-03T17:29:03.7208266Z",
37
+ "ipn_id": "e32182ca-0983-4fa0-91bc-c3bb813ba750",
38
+ "error": null,
39
+ "status": "200"
40
+ },
41
+ {
42
+ "url": "https://ipn.myapplication.com/application2",
43
+ "created_date": "2021-12-05T04:23:45.5509243Z",
44
+ "ipn_id": "c3bb813ba750-0983-4fa0-91bc-e32182ca",
45
+ "error": null,
46
+ "status": "200"
47
+ }
48
+ ]
49
+ ```
knowledge_base/pesapal/pesapal_e-commerce-api-3.0/pesapal_e-commerce_api_3.0_get_transaction_status_docs.md ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Get Transaction Status
2
+
3
+ ## Get Transaction Status Endpoint - Get Request
4
+
5
+ Once Pesapal redirect your customer to your callback URL and triggers your IPN URL, you need to check the status of the payment using the **OrderTrackingId**.
6
+
7
+ The URL to our GetTransactionStatus API is either:
8
+
9
+ - **Sandbox/Demo URL:** https://cybqa.pesapal.com/pesapalv3/api/Transactions/GetTransactionStatus?orderTrackingId=xxxxxxxxxxxx
10
+ - **Production/Live URL:** https://pay.pesapal.com/v3/api/Transactions/GetTransactionStatus?orderTrackingId=xxxxxxxxxxxx
11
+
12
+ Where **xxxxxxxxxxxx** represents the OrderTrackingId received as part of the callback [GET] or IPN [POST/GET] params.
13
+
14
+ ## Authentication
15
+
16
+ **Bearer Token:** Use token generated during authentication.
17
+
18
+ ## HTTP Request Headers
19
+
20
+ **Accept:** The response format, which is required for operations with a response body.
21
+ **Content-Type:** The request format, which is required for operations with a request body.
22
+
23
+ | Parameter | Required | Description |
24
+ |-----------|----------|-------------|
25
+ | Accept | Required | Should be set to application/json |
26
+ | Content-Type | Required | Should be set to application/json |
27
+
28
+ ## Request Parameters
29
+
30
+ | Parameter | Type | Required | Description |
31
+ |-----------|------|----------|-------------|
32
+ | orderTrackingId | string | Required | Unique order id generated by Pesapal e.g 7e6b62d9-883e-440f-a63e-e1105bbfadc3. Attached as a query parameter to the getTransactionStatus endpoint to get status of your submitted order. |
33
+
34
+ ## Response Parameters
35
+
36
+ | Name | Description |
37
+ |------|-------------|
38
+ | payment_method | This refers to the payment method used by your customers to make payment. Can be through CARD, MPESA, MTN, TIGO etc. |
39
+ | amount | Amount paid by the customer. |
40
+ | created_date | Date the payment was made. |
41
+ | confirmation_code | Confirmation code received from the payment provider used. |
42
+ | payment_status_description | Contains the status of the transaction. This will include either INVALID, FAILED, COMPLETED or REVERSED. |
43
+ | description | Description of the payment status. |
44
+ | message | The message shows if transaction request status was processed successfully or not. |
45
+ | payment_account | Masked card number or phone number used during payment. |
46
+ | call_back_url | A valid URL which Pesapal will redirect the client to after payment is made. |
47
+ | status_code | Pesapal status code representing the payment_status_description. 0 - INVALID, 1 - COMPLETED, 2 - FAILED, 3 - REVERSED |
48
+ | merchant_reference | Your application's unique ID as received in the SubmitOrderRequest call. |
49
+ | currency | Currency the payment was made in. ISO Currency Codes. |
50
+ | error | An error object containing error_type, code, message and call_back_url. |
51
+ | status | HTTP status code as defined on RFC 2616. A status of 200 means the request was successful. |
52
+
53
+ ## Sample Response
54
+
55
+ ```json
56
+ {
57
+ "payment_method": "Visa",
58
+ "amount": 100,
59
+ "created_date": "2022-04-30T07:41:09.763",
60
+ "confirmation_code": "6513008693186320103009",
61
+ "payment_status_description": "Failed",
62
+ "description": "Unable to Authorize Transaction.Kindly contact your bank for assistance",
63
+ "message": "Request processed successfully",
64
+ "payment_account": "476173**0010",
65
+ "call_back_url": "https://test.com/?OrderTrackingId=7e6b62d9-883e-440f-a63e-e1105bbfadc3&OrderMerchantReference=1515111111",
66
+ "status_code": 2,
67
+ "merchant_reference": "1515111111",
68
+ "payment_status_code": "",
69
+ "currency": "KES",
70
+ "error": {
71
+ "error_type": null,
72
+ "code": null,
73
+ "message": null,
74
+ "call_back_url": null
75
+ },
76
+ "status": "200"
77
+ }
78
+ ```
79
+
80
+ ## What Next?
81
+
82
+ Once you've received the payment details, you are then required to store the same in your system and provide services / services paid for.
83
+
84
+ Your IPN endpoint should then respond to Pesapal with a json string confirming receipt of the IPN call. Part of the json string contains a **status** parameter that should be set as **200** (meaning request was received and processed) or **500** (meaning IPN request was received but there was an issue completing the process).
85
+
86
+ **Sample IPN JSON Response String:**
87
+
88
+ ```json
89
+ {
90
+ "orderNotificationType": "IPNCHANGE",
91
+ "orderTrackingId": "d0fa69d6-f3cd-433b-858e-df86555b86c8",
92
+ "orderMerchantReference": "1515111111",
93
+ "status": 200
94
+ }
95
+ ```
96
+
97
+ **NB:** Your callback page should not implement a json response. Instead, redirect the customer to a page on your system showing the payment details.
knowledge_base/pesapal/pesapal_e-commerce-api-3.0/pesapal_e-commerce_api_3.0_ipn_docs.md ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # IPN URL Registration
2
+
3
+ ## IPN URL Registration Endpoint - Post Request
4
+
5
+ **IPN** stands for **Instant Payment Notification**. When a payment is made against a transaction, Pesapal will trigger an IPN call to the notification URL related to this transaction. This notification URL is usually located on your servers. These notifications allows you to be alerted in real time whenever there is a status change for any transaction.
6
+
7
+ An IPN is particular important as it allows you to be notified incase the following happens:
8
+
9
+ - Your client gets disconnected after payment due to internet issues
10
+ - Your client experiences server errors hence Pesapal and your application gets disconnected before callback URL is loaded
11
+ - Your client exits your application / closes the browser during payment
12
+ - The transaction is rejected
13
+
14
+ As such, it's mandatory to have IPN configured to allow Pesapal to notify your servers when a status changes. It's also important to note that this **IPN URL must be publicly available**. In cases where you have strict server rules preventing external systems reaching your end, you must then whitelist all calls from our domain (pesapal.com). Please be informed that IP whitelisting is not feasible as our IP may change without notice.
15
+
16
+ Before sending Submit Order Requests to Pesapal API 3.0, you are expected to register your IPN URL. Upon registration, you receive an **IPN ID** which is a mandatory field (**notification_id**) when submitting an order request to Pesapal API 3.0. This **notification_id** uniquely identifies the endpoint Pesapal will send alerts to whenever a payment status changes for each transaction processed via API 3.0
17
+
18
+ The URL to our IPN registration API is either:
19
+
20
+ - **Sandbox/Demo URL:** https://cybqa.pesapal.com/pesapalv3/api/URLSetup/RegisterIPN
21
+ - **Production/Live URL:** https://pay.pesapal.com/v3/api/URLSetup/RegisterIPN
22
+
23
+ ## Authentication
24
+
25
+ **Bearer Token:** Use token generated during authentication.
26
+
27
+ ## HTTP Request Headers
28
+
29
+ **Accept:** The response format, which is required for operations with a response body.
30
+ **Content-Type:** The request format, which is required for operations with a request body.
31
+
32
+ | Parameter | Required | Description |
33
+ |-----------|----------|-------------|
34
+ | Accept | Required | Should be set to application/json |
35
+ | Content-Type | Required | Should be set to application/json |
36
+
37
+ ## Request Parameters
38
+
39
+ | Parameter | Type | Required | Description |
40
+ |-----------|------|----------|-------------|
41
+ | url | String | Required | The notification url Pesapal with send a status alert to. |
42
+ | ipn_notification_type | String | Required | GET or POST. This is the http request method Pesapal will use when triggering the IPN alert. |
43
+
44
+ ## Sample Request
45
+
46
+ ```json
47
+ {
48
+ "url": "https://www.myapplication.com/ipn",
49
+ "ipn_notification_type": "GET"
50
+ }
51
+ ```
52
+
53
+ ## Response Parameters
54
+
55
+ | Name | Description |
56
+ |------|-------------|
57
+ | url | The notification url Pesapal will send a status alert to. |
58
+ | created_date | Date and time the IPN URL was registered UTC |
59
+ | ipn_id | A unique identifier that's linked to the IPN endpoint URL. GUID |
60
+ | notification_type | 1 or 0 |
61
+ | ipn_notification_type_description | The http request method you registered your IPN url as |
62
+ | ipn_status | 1 or 0 ie Active or Inactive |
63
+ | ipn_status_decription | Status description ie Active or Inactive |
64
+ | error | An error object containing error_type, code and message if any. Null to signify no error. |
65
+ | status | Response code. |
66
+
67
+ ## Sample Response
68
+
69
+ ```json
70
+ {
71
+ "url": "https://myapplication.com/ipn",
72
+ "created_date": "2024-06-14T07:50:22.2825997Z",
73
+ "ipn_id": "84740ab4-3cd9-47da-8a4f-dd1db53494b5",
74
+ "notification_type": 0,
75
+ "ipn_notification_type_description": "GET",
76
+ "ipn_status": 1,
77
+ "ipn_status_description": "Active",
78
+ "error": null,
79
+ "status": "200"
80
+ }
81
+ ```
82
+
83
+ ## Alternative Registration Methods
84
+
85
+ You can also use our online forms below to register your IPN URLs:
86
+
87
+ - **Sandbox/Demo IPN Registration Form**
88
+ - **Production/Live IPN Registration Form**
knowledge_base/pesapal/pesapal_e-commerce-api-3.0/pesapal_e-commerce_api_3.0_order_cancellation_api_docs.md ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Order Cancellation API
2
+
3
+ ## Definitions
4
+
5
+ The Order Cancellation API enables you to revoke a previously placed order request that remains incomplete on our end. This API facilitates the cancellation of orders that have encountered failures or are pending transactions.
6
+
7
+ However, there are certain constraints associated with cancelling a prior request:
8
+
9
+ 1. Cancellation is exclusively supported for failed or pending payments.
10
+ 2. A cancellation request can only be submitted once.
11
+ 3. Cancellation is not allowed for payments that have already been processed.
12
+
13
+ You can access our Cancel Order API via the endpoints provided below:
14
+
15
+ - **Sandbox/Demo URL:** https://cybqa.pesapal.com/pesapalv3/api/Transactions/CancelOrder
16
+ - **Production/Live URL:** https://pay.pesapal.com/v3/api/Transactions/CancelOrder
17
+
18
+ ## Authentication
19
+
20
+ **Bearer Token:** Use token generated during authentication.
21
+
22
+ ## HTTP Request Headers
23
+
24
+ **Accept:** The response format, which is required for operations with a response body.
25
+ **Content-Type:** The request format, which is required for operations with a request body.
26
+
27
+ | Parameter | Required | Description |
28
+ |-----------|----------|-------------|
29
+ | Accept | Required | Should be set to `application/json` |
30
+ | Content-Type | Required | Should be set to `application/json` |
31
+
32
+ ## Request Parameters
33
+
34
+ | Parameter | Type | Required | Description |
35
+ |-----------|------|----------|-------------|
36
+ | order_tracking_id | Guid | Required | This refers to the original Pesapal Order tracking ID that was returned after submitting your order request earlier during the initial submit order request api call. |
37
+
38
+ ## Sample Request
39
+
40
+ ```json
41
+ {
42
+ "order_tracking_id": "xxxxxxxxxxxxxxxxxxxxxxx"
43
+ }
44
+ ```
45
+
46
+ After successfully cancelling the request, the order shall be cancelled on our systems preventing a customer from making further payments to an already cancelled order.
47
+
48
+ ## Response Parameters
49
+
50
+ | Name | Type | Description |
51
+ |------|------|-------------|
52
+ | status | message | 200 – Order Successfully cancelled. 500 – Order could not be cancelled. |
53
+ | message | String | A summary of the response received. |
54
+
55
+ Status **200** mean your request to cancel the order has been processed successfully.
56
+
57
+ Status **500** mean your request to cancel your order has failed due to one or more reasons as shared earlier on the introduction page above.
58
+
59
+ ## Sample Response
60
+
61
+ ```json
62
+ {
63
+ "status": "200",
64
+ "message": "Order successfully cancelled."
65
+ }
66
+ ```
67
+
68
+ **Important Note:** The Order Cancellation API uses the Pesapal Order Tracking ID to cancel the order. It's important that you store the Pesapal Order Tracking ID that is sent back to your system during your initial order placement stage i.e. on the submit order request endpoint.
knowledge_base/pesapal/pesapal_e-commerce-api-3.0/pesapal_e-commerce_api_3.0_recurring_payments_docs.md ADDED
@@ -0,0 +1,217 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Pesapal Recurring / Subscription Based Payments
2
+
3
+ ## Definitions
4
+
5
+ **Recurring payment** is a payment model where customers authorize the business to pull funds from their accounts automatically at regular intervals for the goods and services provided to them on an ongoing basis.
6
+
7
+ With Pesapal's subscription based payments, customers can set automated card payments on their account where they can be debited automatically at a different set times. Examples of services one can enroll to include cable bills, cell phone bills, gym membership fees, utility bills and magazine subscriptions. These payments can be set to run an various intervals such as daily, weekly, monthly or yearly.
8
+
9
+ ## How can recurring payments benefit your business?
10
+
11
+ - **Saves customers time**: Customers no longer have to go through the payment process every time they need to make a payment.
12
+ - **Ensures prompt payment**: Businesses no longer need to worry about getting paid on time. Since the payments are automated, they no longer need to send out overdue payment reminders.
13
+ - **Boosts customer loyalty**: With a subscription model, businesses can form closer relationships with their customers.
14
+ - **Cash flow prediction**: Businesses can easily predict their cash flow, which helps with business strategy.
15
+ - **Lowers billing and collection costs**: Businesses no longer need to chase after missed payments, freeing up their time to concentrate on other elements of the business.
16
+
17
+ ## How do I enable recurring payment for my customers?
18
+
19
+ Enable our subscription based payments by passing an additional **account_number** field when sending data to our **SubmitOrderRequest** endpoint. This account_number should relate to an account number / invoice number that helps you identify the payment.
20
+
21
+ **Note**: It's critical that you get to understand all other Pesapal API 3.0 endpoints before implementing the recurring feature.
22
+
23
+ In addition to the **SubmitOrderRequest** parameters, include one more param as shown below.
24
+
25
+ ### Parameters
26
+
27
+ | Parameter | Required | Description |
28
+ |-----------|----------|-------------|
29
+ | account_number | Optional | Customer's identification number known to your system. This can be an invoice number or an account number. |
30
+
31
+ ### Sample Request
32
+
33
+ ```json
34
+ {
35
+ "id": "AA1122-3344ZZ",
36
+ "currency": "KES",
37
+ "amount": 100.00,
38
+ "description": "Payment description goes here",
39
+ "callback_url": "https://www.myapplication.com/response-page",
40
+ "notification_id": "fe078e53-78da-4a83-aa89-e7ded5c456e6",
41
+ "billing_address": {
42
+ "email_address": "john.doe@example.com",
43
+ "phone_number": "0723xxxxxx",
44
+ "country_code": "KE",
45
+ "first_name": "John",
46
+ "middle_name": "",
47
+ "last_name": "Doe",
48
+ "line_1": "Pesapal Limited",
49
+ "line_2": "",
50
+ "city": "",
51
+ "state": "",
52
+ "postal_code": "",
53
+ "zip_code": ""
54
+ },
55
+ "account_number": "555-678"
56
+ }
57
+ ```
58
+
59
+ After successfully processing your request using the **SubmitOrderRequest** endpoint, the customer will be shown an option to opt into the recurring model on the Pesapal iframe during payment. The customer will then configure the frequency (Daily, weekly, monthly, quarterly or yearly), set a start and enddate, and finally enter an amount to be automatically deducted from their card on each payment cycle.
60
+
61
+ Once the customer has made a successful payment, Pesapal will automatically create a scheduled subscription on their behalf and an email alert will be sent to the provided card billing email that was used during the payment process, notifying them of the newly created subscription together with a link they can access a dashboard to manage their subscription.
62
+
63
+ **Pesapal will NOT store the customer's card details.** Instead, Pesapal has implemented the **card tokenization technology**.
64
+
65
+ **Credit card tokenization** is the process of de-identifying sensitive cardholder data by converting it to a string of randomly generated numbers called a "token." Similar to encryption, tokenization obfuscates the original data to render it unreadable to a user. Unlike encryption, however, credit card tokenization is irreversible.
66
+
67
+ ## Is it possible to send the extra subscription parameters (Frequency, amount, period) via the API?
68
+
69
+ Yes, in cases where your application already handles the process where the customer opts into a subscription based model from your application, Pesapal allows you to send these extra parameters via the API. This ensures that the user does not have to fill in the same details again (on your application and on the Pesapal Iframe).
70
+
71
+ However, it's important to note that the customer **MUST accept to enroll to your subscription on the iframe**. They will however not be able to edit the subscription details on the iframe.
72
+
73
+ In addition to the **account_number** parameter included in the **SubmitOrderRequest**, you are required to send the following parameter.
74
+
75
+ ### Parameters
76
+
77
+ | Parameter | Required | Description |
78
+ |-----------|----------|-------------|
79
+ | subscription_details | Optional | Customer Subscription Object You can pass subscription data to Pesapal allowing a user to setup recurring payment. |
80
+
81
+ ### Subscription Object
82
+
83
+ | Name | Type | Required | Description |
84
+ |------|------|----------|-------------|
85
+ | start_date | String | Mandatory | Your subscription's start date in the format dd-MM-yyyy e.g 24-01-2023 representing 24th Jan 2023 |
86
+ | end_date | String | Mandatory | Your subscription's end date in the format dd-MM-yyyy e.g 31-12-2023 representing 31st Dec 2023 |
87
+ | frequency | String | Mandatory | The period billed to the account is set out in the user contract. For instance, if users subscribe to a monthly service. Accepted values include DAILY, WEEKLY, MONTHLY or YEARLY |
88
+
89
+ ### Sample Request
90
+
91
+ ```json
92
+ {
93
+ "id": "AA1122-3344ZZ",
94
+ "currency": "KES",
95
+ "amount": 100.00,
96
+ "description": "Payment description goes here",
97
+ "callback_url": "https://www.myapplication.com/response-page",
98
+ "notification_id": "fe078e53-78da-4a83-aa89-e7ded5c456e6",
99
+ "billing_address": {
100
+ "email_address": "john.doe@example.com",
101
+ "phone_number": "0723xxxxxx",
102
+ "country_code": "KE",
103
+ "first_name": "John",
104
+ "middle_name": "",
105
+ "last_name": "Doe",
106
+ "line_1": "Pesapal Limited",
107
+ "line_2": "",
108
+ "city": "",
109
+ "state": "",
110
+ "postal_code": "",
111
+ "zip_code": ""
112
+ },
113
+ "account_number": "555-678",
114
+ "subscription_details": {
115
+ "start_date": "24-01-2023",
116
+ "end_date": "31-12-2023",
117
+ "frequency": "DAILY"
118
+ }
119
+ }
120
+ ```
121
+
122
+ After successfully processing your request using the **SubmitOrderRequest** endpoint, the customer will be shown an option to opt into the recurring model on the Pesapal iframe during payment. The iframe will this time load without the options to re-select the Frequency, period and dates.
123
+
124
+ ## Can a customer opt out of recurring payments before the end date?
125
+
126
+ Yes, customers have the right to cancel their subscription at anytime. Pesapal will send them email alerts a day or two prior to charging their cards. This ensures customers are always aware of all upcoming charges giving them the freedom to opt out or pausing their subscriptions.
127
+
128
+ ## Which cards are currently supported?
129
+
130
+ We currently support **Visa** and **MasterCard** for recurring payments. More card options will be enabled in the near future.
131
+
132
+ ## How will Pesapal alert my business about successful recurring payments?
133
+
134
+ Once a schedule is created and executed successfully, Pesapal will trigger an **IPN (Instant Payment Notification)** to the IPN endpoint you provided when calling the **SubmitOrderRequest** endpoint. This IPN call will have the **OrderNotificationType** set as **RECURRING**.
135
+
136
+ **Sample IPN URL**:
137
+ ```
138
+ https://www.myapplication.com/response-page?OrderTrackingId=b945e4af-80a5-4ec1-8706-e03f8332fb04&OrderMerchantReference=555-678&OrderNotificationType=RECURRING
139
+ ```
140
+
141
+ ### Recurring IPN Details
142
+
143
+ The IPN alert will either be a **GET** or **POST** request, depending on which HTTP method you selected when registering the IPN URL.
144
+
145
+ The IPN call will have the following parameters:
146
+
147
+ | Parameter | Type | Description |
148
+ |-----------|------|-------------|
149
+ | OrderTrackingId | String | Unique order id generated by Pesapal. |
150
+ | OrderNotificationType | String | Value set as RECURRING to represent a Recurring IPN call. |
151
+ | OrderMerchantReference | String | Your account number received as part of the SUBMIT ORDER REQUEST. |
152
+
153
+ The IPN call will **NOT** have all details of the payment for security reasons. As such, you will be required to fetch the payment using the **GetTransactionStatus API** once the IPN URL is triggered.
154
+
155
+ In addition to the normal payment status details received from the **GetTransactionStatus** endpoint, Pesapal will append an object **subscription_transaction_info** containing some additional recurring payment data.
156
+
157
+ ### Recurring Payments Extra Data (subscription_transaction_info)
158
+
159
+ | Name | Description |
160
+ |------|-------------|
161
+ | account_reference | Customer's identification number known to your system. This can be an invoice number or an account number. |
162
+ | amount | Amount paid by the customer. |
163
+ | first_name | Customer's first name. |
164
+ | last_name | Customer's last name. |
165
+ | correlation_id | Pesapal's unique recurring payment identifier / id. |
166
+
167
+ ### Sample Response
168
+
169
+ ```json
170
+ {
171
+ "payment_method": "Visa",
172
+ "amount": 100,
173
+ "created_date": "2022-04-30T07:41:09.763",
174
+ "confirmation_code": "6513008693186320103009",
175
+ "payment_status_description": "Failed",
176
+ "description": "Unable to Authorize Transaction.Kindly contact your bank for assistance",
177
+ "message": "Request processed successfully",
178
+ "payment_account": "476173**0010",
179
+ "call_back_url": "https://test.com/?OrderTrackingId=7e6b62d9-883e-440f-a63e-e1105bbfadc3&OrderMerchantReference=555-678",
180
+ "status_code": 2,
181
+ "merchant_reference": "1515111111",
182
+ "payment_status_code": "",
183
+ "currency": "KES",
184
+ "subscription_transaction_info": {
185
+ "account_reference": "555-678",
186
+ "amount": 100,
187
+ "first_name": "John",
188
+ "last_name": "Doe",
189
+ "correlation_id": 111222
190
+ },
191
+ "error": {
192
+ "error_type": null,
193
+ "code": null,
194
+ "message": null,
195
+ "call_back_url": null
196
+ },
197
+ "status": "200"
198
+ }
199
+ ```
200
+
201
+ ## What Next?
202
+
203
+ Once you've fetched the recurring data, you are then required to store the same in your system and provide services / goods as subscribed.
204
+
205
+ Your IPN endpoint should then respond to Pesapal with a json string confirming service delivery. Part of the json string contains a **status** parameter that should be set as **200** (meaning request was received and processed) or **500** (meaning request was received but there was an issue providing the services).
206
+
207
+ **Sample JSON Response String**:
208
+ ```json
209
+ {
210
+ "orderNotificationType": "RECURRING",
211
+ "orderTrackingId": "d0fa69d6-f3cd-433b-858e-df86555b86c8",
212
+ "orderMerchantReference": "555-678",
213
+ "status": 200
214
+ }
215
+ ```
216
+
217
+ **NB**: Constant complaints from your customers about service / goods not delivered which were paid using recurring / subscription based payment mode will lead to the subscriptions being terminated and your profile banned from using the feature.
knowledge_base/pesapal/pesapal_e-commerce-api-3.0/pesapal_e-commerce_api_3.0_refund_api_docs.md ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Refund Request
2
+
3
+ ## Definitions
4
+
5
+ The refund request endpoint allow you to refund a charge that has previously been processed but not yet refunded. Funds will be refunded to the credit / debit card or mobile money wallet that was originally charged.
6
+
7
+ The ability to process a refund has the following limitations:
8
+
9
+ 1. A refund has to be approved by the merchant.
10
+ 2. You can't refund more than what was originally collected.
11
+ 3. You can only refund payments with the status of **COMPLETED**.
12
+ 4. You can partially or fully refund a payment card payment.
13
+ 5. You can only fully refund a payment mobile payment.
14
+ 6. Refunds are performed in the currency of the original payment.
15
+ 7. Multiple refunds are not allowed. You can only request one refund against a payment.
16
+
17
+ The URL to our RequestRefund API is either:
18
+
19
+ - **Sandbox/Demo URL:** https://cybqa.pesapal.com/pesapalv3/api/Transactions/RefundRequest
20
+ - **Production/Live URL:** https://pay.pesapal.com/v3/api/Transactions/RefundRequest
21
+
22
+ ## Authentication
23
+
24
+ **Bearer Token:** Use token generated during authentication.
25
+
26
+ ## HTTP Request Headers
27
+
28
+ **Accept:** The response format, which is required for operations with a response body.
29
+ **Content-Type:** The request format, which is required for operations with a request body.
30
+
31
+ | Parameter | Required | Description |
32
+ |-----------|----------|-------------|
33
+ | Accept | Required | Should be set to `application/json` |
34
+ | Content-Type | Required | Should be set to `application/json` |
35
+
36
+ ## Request Parameters
37
+
38
+ | Parameter | Type | Required | Description |
39
+ |-----------|------|----------|-------------|
40
+ | confirmation_code | String | Required | This refers to payment confirmation code that was returned by the processor |
41
+ | amount | Float | Required | Amount to be refunded. |
42
+ | username | String | Required | Identity of the user who has initiated the refund. |
43
+ | remarks | String | Required | A brief description on the reason for the refund. |
44
+
45
+ ## Sample Request
46
+
47
+ ```json
48
+ {
49
+ "confirmation_code": "AA11BB22",
50
+ "amount": "100.00",
51
+ "username": "John Doe",
52
+ "remarks": "Service not offered"
53
+ }
54
+ ```
55
+
56
+ After successfully placing a refund request, a request will be sent to our finance team to start processing the refund.
57
+
58
+ ## Response Parameters
59
+
60
+ | Name | Type | Description |
61
+ |------|------|-------------|
62
+ | error | Integer | 200 - Refund received successfully and is being processed. 500 - Refund rejected. |
63
+ | message | String | A brief summary of the response received. |
64
+
65
+ Status **200** mean your request to process the refund has been received successfully. However, please note that this does not mean the refund has been effected. Pesapal has to get the go ahead from the merchant before finalising the refund.
66
+
67
+ Status **500** mean your request to process the refund was rejected for one reason or another. Refer to the error message for more details.
68
+
69
+ ## Sample Response
70
+
71
+ ```json
72
+ {
73
+ "status": "200",
74
+ "message": "Refund request successfully"
75
+ }
76
+ ```
77
+
78
+ **Important Note:** The refund API uses the confirm code for identification. It's important that you store all payment confirmation codes as returned in the Get Transaction Status Endpoint.
knowledge_base/pesapal/pesapal_e-commerce-api-3.0/pesapal_e-commerce_api_3.0_submit_order_docs.md ADDED
@@ -0,0 +1,165 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Submit Order Request
2
+
3
+ ## Submit Order Request Endpoint - Post Request
4
+
5
+ Once you have received the bearer token, the next step will be the actual request to create a payment request.
6
+
7
+ A good example would be a case where the customer has clicked pay now button on your website. At this point, you call the **SubmitOrderRequest** and in return you will get a response which contains a payment redirect URL which you then redirect the customer to or load the URL as an iframe within your site in case you don't want to redirect the customer off your application.
8
+
9
+ The payment URL will contain the payment methods presented to the customer by Pesapal. After the customer has made the payment, they will be redirected to your callback URL which you will have already provided to us as part of submit order request.
10
+
11
+ The URL to our SubmitOrderRequest API is either:
12
+
13
+ - **Sandbox/Demo URL:** https://cybqa.pesapal.com/pesapalv3/api/Transactions/SubmitOrderRequest
14
+ - **Production/Live URL:** https://pay.pesapal.com/v3/api/Transactions/SubmitOrderRequest
15
+
16
+ ## Authentication
17
+
18
+ **Bearer Token:** Use token generated during authentication.
19
+
20
+ ## HTTP Request Headers
21
+
22
+ **Accept:** The response format, which is required for operations with a response body.
23
+ **Content-Type:** The request format, which is required for operations with a request body.
24
+
25
+ | Parameter | Required | Description |
26
+ |-----------|----------|-------------|
27
+ | Accept | Required | Should be set to application/json |
28
+ | Content-Type | Required | Should be set to application/json |
29
+
30
+ ## Request Parameters
31
+
32
+ | Parameter | Type | Required | Description |
33
+ |-----------|------|----------|-------------|
34
+ | id | String | Required | This refers to your unique merchant reference generated by your system. NOTE: It should be unique at all times for every order request. Maximum - 50 characters |
35
+ | currency | String | Required | This represents the currency you want to charge your customers. ISO Currency Codes |
36
+ | amount | Float | Required | Amount to be processed. |
37
+ | description | String | Required | Represents the description of your order. Maximum - 100 characters |
38
+ | redirect_mode | String | Optional | Accepts values TOP_WINDOW or PARENT_WINDOW. If left blank, the default value used will be TOP_WINDOW. This parameter allows you to define where your callback URL will be loaded; - TOP_WINDOW returns to the topmost window in the hierarchy of windows. - PARENT_WINDOW returns the immediate parent of a window. |
39
+ | callback_url | String | Required | A valid URL which Pesapal will redirect your clients to processing the payment. |
40
+ | cancellation_url | String | Optional | A valid URL which Pesapal will redirect your clients to incase they click on cancel request while on the Payment link. If provided, a cancellation link will appear next to the PROCEED button. |
41
+ | notification_id | Guid | Required | This represents an IPN URL which Pesapal will send notifications to after payments have been processed. You are required to register all IPN URLs after which a corresponding notification_id will be generated. Please refer to the IPN URL registration endpoint. |
42
+ | branch | String | Optional | If your business has multiple stores / branches, you can define the name of the store / branch to which this particular payment will be accredited to. |
43
+ | billing_address | Object | Required | Customer Address Object |
44
+
45
+ ## Customer Address Object
46
+
47
+ | Name | Type | Required | Description |
48
+ |------|------|----------|-------------|
49
+ | phone_number | String | Optional if email address is provided. Mandatory if email address is NOT provided. | Customer's phone number |
50
+ | email_address | String | Optional if phone number is provided. Mandatory if phone number is NOT provided. | Customer's email address |
51
+ | country_code | String | Optional | 2 characters long country code in [ISO 3166-1](https://en.wikipedia.org/wiki/ISO_3166-1) |
52
+ | first_name | String | Optional | Customer's first name |
53
+ | middle_name | String | Optional | Customer's middle name |
54
+ | last_name | String | Optional | Customer's last name |
55
+ | line_1 | String | Optional | Customer's main address |
56
+ | line_2 | String | Optional | Customer's alternative address |
57
+ | city | String | Optional | Customer's city |
58
+ | state | String | Optional | Customer's state Maximum - 3 characters |
59
+ | postal_code | Integer | Optional | Customer's postal code |
60
+ | zip_code | Integer | Optional | Customer's zip code |
61
+
62
+ ## Sample Request
63
+
64
+ ```json
65
+ {
66
+ "id": "AA1122-3344ZZ",
67
+ "currency": "KES",
68
+ "amount": 100.00,
69
+ "description": "Payment description goes here",
70
+ "callback_url": "https://www.myapplication.com/response-page",
71
+ "redirect_mode": "",
72
+ "notification_id": "fe078e53-78da-4a83-aa89-e7ded5c456e6",
73
+ "branch": "Store Name - HQ",
74
+ "billing_address": {
75
+ "email_address": "john.doe@example.com",
76
+ "phone_number": "0723xxxxxx",
77
+ "country_code": "KE",
78
+ "first_name": "John",
79
+ "middle_name": "",
80
+ "last_name": "Doe",
81
+ "line_1": "Pesapal Limited",
82
+ "line_2": "",
83
+ "city": "",
84
+ "state": "",
85
+ "postal_code": "",
86
+ "zip_code": ""
87
+ }
88
+ }
89
+ ```
90
+
91
+ After successfully creating the order, your response will contain a payment URL (**redirect_url**).
92
+
93
+ **Status 200** mean your request to process the payment has been received successfully meaning the order has been successfully created on our systems and you can now redirect your customer to make the payment.
94
+
95
+ ## Response Parameters
96
+
97
+ | Name | Type | Description |
98
+ |------|------|-------------|
99
+ | order_tracking_id | String | Unique order id generated by Pesapal. |
100
+ | merchant_reference | String | Your application's unique ID received as part of the SUBMIT ORDER REQUEST. |
101
+ | redirect_url | String | URL generated that contains the payment instructions. Redirect to this URL or load it within an iframe |
102
+ | error | Integer | |
103
+ | message | String | Response message. |
104
+
105
+ ## Sample Response
106
+
107
+ ```json
108
+ {
109
+ "order_tracking_id": "b945e4af-80a5-4ec1-8706-e03f8332fb04",
110
+ "merchant_reference": "TEST1515111119",
111
+ "redirect_url": "https://cybqa.pesapal.com/pesapaliframe/PesapalIframe3/Index/?OrderTrackingId=b945e4af-80a5-4ec1-8706-e03f8332fb04",
112
+ "error": null,
113
+ "status": "200"
114
+ }
115
+ ```
116
+
117
+ Redirecting your customer to this **redirect_url** will present them with a list of available payment methods which they can use to make the payment.
118
+
119
+ You can either load this URL outside or within your website (using an Iframe). The advantage of using an iframe is that your customer will not be redirected off the site, hence a seamless payment process.
120
+
121
+ Once your customer selects their preferred payment method and makes a payment, Pesapal will process the payment and redirect your customers to the callback URL which you had provided as part of the SubmitOrderRequest API call.
122
+
123
+ ## Callback Details
124
+
125
+ The following parameter will be appended to your callback URL.
126
+
127
+ | Parameter | Type | Description |
128
+ |-----------|------|-------------|
129
+ | OrderTrackingId | String | Unique order id generated by Pesapal. |
130
+ | OrderNotificationType | String | Has value CALLBACKURL to represent a callback request |
131
+ | OrderMerchantReference | String | Your application's unique ID received as part of the SUBMIT ORDER REQUEST. |
132
+
133
+ **Sample Callback URL:** https://www.myapplication.com/response-page?OrderTrackingId=b945e4af-80a5-4ec1-8706-e03f8332fb04&OrderMerchantReference=TEST1515111119&OrderNotificationType=CALLBACKURL
134
+
135
+ ## IPN Details
136
+
137
+ At the same time, Pesapal will send an alert to your IPN URL informing you of a status change.
138
+
139
+ The IPN alert will either be a GET or POST request, depending on which HTTP method you selected when registering the IPN URL.
140
+
141
+ The IPN call will have the following parameters:
142
+
143
+ | Parameter | Type | Description |
144
+ |-----------|------|-------------|
145
+ | OrderTrackingId | String | Unique order id generated by Pesapal. |
146
+ | OrderNotificationType | String | Has value IPNCHANGE to represent an IPN call. |
147
+ | OrderMerchantReference | String | Your application's unique ID received as part of the SUBMIT ORDER REQUEST. |
148
+
149
+ ### Sample GET URL
150
+
151
+ ```
152
+ https://www.myapplication.com/response-page?OrderTrackingId=b945e4af-80a5-4ec1-8706-e03f8332fb04&OrderMerchantReference=TEST1515111119&OrderNotificationType=IPNCHANGE
153
+ ```
154
+
155
+ ### Sample POST data
156
+
157
+ ```json
158
+ {
159
+ "OrderNotificationType":"IPNCHANGE",
160
+ "OrderTrackingId":"b945e4af-80a5-4ec1-8706-e03f8332fb04",
161
+ "OrderMerchantReference":"TEST1515111119"
162
+ }
163
+ ```
164
+
165
+ The callback URL and the IPN calls will **NOT** have the status of the payment for security reasons. As such, you will be required to fetch the status of the payment using the **GetTransactionStatus API** once the callback page is loaded and when the IPN URL is triggered.
knowledge_base/pesapal/pesapal_integration_guide.md ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Integration Guide
2
+
3
+ We have outlined below the steps involved in integrating a website with PesaPal. To view samples and the complete API reference, please click **here**.
4
+
5
+ ## Step 1: Build your site / application that will accept payments from your customers
6
+
7
+ The site / application will:
8
+
9
+ - Collect customer information, such as First Name, Last Name and Email Address
10
+ - Collect payment information, such as Amount and Currency
11
+
12
+ **Please Note:** the information you collect will be dependent on your site / application requirements. But some information is required by PesaPal
13
+
14
+ ## Step 2: Package the request to post to PesaPal
15
+
16
+ Once you have the customer and payment details, you need to package it to send to PesaPal.
17
+
18
+ This involves a few steps as we need to ensure that the communication between your site and PesaPal happens in a secure manner.
19
+
20
+ PesaPal uses **JSON Web Tokens (JWT)** to make sure that no one else interfers with the request you post to PesaPal
21
+
22
+ ## Step 3: Post the request to PesaPal and load the PesaPal payments page
23
+
24
+ When a request is posted, PesaPal will display a payments page. This is where the customer will make the payment.
25
+
26
+ You can embed this payments page directly in your site, providing a seamless experience to your customers. You can do this by inserting an IFrame on the page on your site customers land on when they click pay or checkout.
27
+
28
+ ## Step 4: Display a post-payment page to your customer
29
+
30
+ Once the customer has completed the payment proces on PesaPal, they will be redirected to a page on your site.
31
+
32
+ You can use this page to inform the customer that their payment is being processed.
33
+
34
+ Optionally, you can also query PesaPal at this point to see if the payment has completed succesfully, has failed, or is still being processed.
35
+
36
+ ## Step 5: Query PesaPal for payment status
37
+
38
+ One last step! When you receive an IPN notification from PesaPal, you need to query PesaPal for the payment status using the orderTrackingId that was sent with the notification
39
+
40
+ A **PENDING** status indicates that the payment is being processed by PesaPal and the final status of payment (**COMPLETED** or **FAILED**) is not yet known.
knowledge_base/pesapal/pesapal_plugins/pesapal_plugin_installation_guides.md ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Pesapal Plugin Installation Guides
2
+
3
+ ## PrestaShop Installation Guide
4
+
5
+ ### Step 1
6
+ - Download our pesapal_prestashop_module_8.1.0 zipped file, unzip it, then inside the file locate the Pesapal folder and zip it.
7
+
8
+ ### Step 2
9
+ - Log in to your PrestaShop administration panel (e.g., http://yourdomain.com/admin).
10
+
11
+ ### Step 3
12
+ - Go to Modules, Module Manager. Click on the "Upload a Module" button.
13
+
14
+ ### Step 4
15
+ - Browse for the module's downloaded compressed file on your local machine and select the Pesapal zipped file from Step 1.
16
+
17
+ ### Step 5
18
+ - After a successful upload, "Module installed!" click on "Configure" to finish the installation.
19
+
20
+ - **Live:** Enter the Consumer Key & Consumer Secret. You should have received these from the Pesapal registration email. If you lost them, log in to your Pesapal merchant account, scroll to the bottom of the dashboard to find them.
21
+ - **Demo:** To use our Demo, you will have to use our demo consumer keys and secret available here: https://developer.pesapal.com/api3-demo-keys.txt
22
+
23
+ - Save Settings, then proceed to test the gateway on your store.
24
+
25
+ ---
26
+
27
+ ## WooCommerce Installation Guide
28
+
29
+ This detailed step by step process below explains how to add Pesapal to your woocommerce Shop to enable you to accept payments using Mobile money (MPESA, Airtel, Voda MPESA, Tigo Pesa, Various Mobile banking apps) and Cards (Visa, MasterCard and American Express).
30
+
31
+ ### Step 1: Download the plugin
32
+
33
+ Download our Pesapal woo-commerce plugin from our official plugins to a known location, preferably your desktop for easy browsing.
34
+
35
+ **PLEASE NOTE:** We recommend you download only the WooCommerce plugin to avoid confusion when setting up. Our latest WooCommerce version to date is v3.1.3
36
+
37
+ ### Step 2: Install the plugin
38
+
39
+ 1. Login in to the admin side of your website. Your website admin link is usually looks like this *https://yourwebsite.com/wp-admin*.
40
+ 2. Point to Plugins >> Select "Add New" to install the plugin.
41
+ 3. Select "Upload Plugin" to browse for the plugin from the location you had previously saved the plugin to during step 1 above.
42
+ 4. After browsing for the file, select "Install Now".
43
+ 5. After Installing the plugin, Activate it.
44
+ 6. From your navigation bar, point to WooCommerce >> Settings. From the settings page, select "Payments" Tab
45
+ 7. You will be able to see Pesapal among the list of payment options you have already added.
46
+ 8. Select "Manage" to enable and configure/set the integration keys
47
+ 9. Enable the plugin by checking the "Enable Pesapal Payment" option.
48
+ 10. Enter your live consumer and secret keys as was sent on your email upon your account creation. If you intend to use demo keys, check the "Use Demo Gateway" option.
49
+ 11. Make sure you save your changes for you be to be able to receive payments through Pesapal
50
+
51
+ To set email notifications for IPN, scroll down the page and enter the email addresses you would like to receive IPN notification. **Please note** that this step is optional. Only use it if you wish to track your IPN call
52
+
53
+ **Note** When payments are completed, you can choose between "Completed" and "Processing" for your WooCommerce order status when setting up our plugin. Simply go to WooCommerce settings > Payments > Pesapal>Mange>Update Paid Orders To to make your selection. This gives you more control over your order management.
54
+
55
+ ### Step 3: Confirm all is well
56
+
57
+ To do so, go to your shop and checkout a product to see if Pesapal payment option appears
58
+
59
+ You can now select "Place Order" to see the Pesapal Payment options listed above
60
+
61
+ ---
62
+
63
+ ## Magento Installation Guide
64
+
65
+ ### Step 1: Install the Plugin
66
+
67
+ 1. Copy the Pesapal folder into to app/code directory
68
+ 2. Run the magento setup upgrade function: bin/magento setup:upgrade
69
+
70
+ Should the upgrade function fail, click here for a possible solution.
71
+
72
+ ### Step 2: Setup plugin and complete Configurations
73
+
74
+ Once you have done this, you need to follow these steps to get it working:
75
+
76
+ 1. Create a merchant account (Business account) at https://www.pesapal.com. After registration, Pesapal will send you an email with you consumer keys and consumer secret needed to use on the website.
77
+ 2. Log into your Magento admin and Clear your cache
78
+
79
+ Go to System or Stores -> Configuration -> Sales -> Payment Methods and you will see. "Pesapal Express"
80
+
81
+ 3. Set your configurations as below;
82
+ - **Enabled:** YES.
83
+ - **Test API:** NO (If you set this to yes, this means you are using our demo Pesapal API. You will have to use our demo consumer keys and secret available here: https://developer.pesapal.com/api3-demo-keys.txt)
84
+ - **Consumer Key & Consumer Secret:** You should have received this from the pesapal registration mail . If you lose this, login to your Pesapal merchant account, scroll to the bottom of the dashboard to find them.
85
+ - **New order status:** This is the default order status set when a user selects pesapal to "processing". All orders with this status mean the user created an order but pending the payment.
86
+
87
+ 4. Save configurations
88
+
89
+ ---
90
+
91
+ ## WHMCS Installation Guide
92
+
93
+ Please follow the instructions below to setup the PesaPal gateway module.
94
+
95
+ ### Note
96
+
97
+ 1. The PesaPal gateway module requires the use of modified templates that are provided with this distribution.
98
+ 2. The templates provided are based on WHMCS 7.1.X ++ and the default theme.
99
+
100
+ ### Step 1: Install the WHMCS Plugin
101
+
102
+ 1. Upload pesapal.php and the pesapal folder to your whmcs's modules/gateways folder.
103
+ 2. Upload the callback/pesapal.php file to your whmcs's modules/gateways/callback folder.
104
+ 3. Upload templates/pesapal_callback.tpl and templates/pesapal_iframe.tpl to your template directory. This usually under : templates/six/
105
+ 4. Enable the PesaPal module in the WHMCS admin area by going to Addons-> Apps & Integrations->Browse->Payments(On the left)->View All->Under Additional Apps click on pesapal to activate then manage. Paste in your administrator username, Consumer Key and Consumer Secret.
106
+ 5. To get your consumer Key and Consumer Secret, Open a Pesapal business account on www.pesapal.com or Pesapal test credentials.
107
+ - If you opened an account on www.pesapal.com(live account), the key and secret have been sent to the email address you registered with.
108
+ - Find Pesapal test credentials here, https://developer.pesapal.com/api3-demo-keys.txt
109
+ - Ensure when you are done testing the plugin using the demo/sandbox account you switch to the live API.
110
+ 6. Save configurations.
111
+
112
+ **NB:** Do not change display name in the configuration.
113
+
114
+ ---
115
+
116
+ ## Shopify Installation Guide
117
+
118
+ This reading will guide you on how to setup your Shopify Store to accept online payments with Pesapal. Upon completion, your customers will be able to checkout online and pay with the payment options supported in your country such as Visa, Master Card, Amex, MPESA, Airtel Money, Tigo Pesa and others.
119
+
120
+ ### Step 1 - Ensure you have a Pesapal Account
121
+
122
+ If you already have an account with www.pesapal.com go to Step 2.
123
+
124
+ In case you have not yet created a Pesapal Business account, you can create a new PesaPal Business account by registering here: Create a Business Account
125
+
126
+ If you would like guidance on creating an account, you may contact us here
127
+
128
+ ### Step 2 - Find your Consumer Key and Secret
129
+
130
+ Go to the email address you used to register your Pesapal Business account and search for "**pesapal integration information**". The email should look similar to this:
131
+
132
+ In case you cannot find this email, you can resend it by going to your Pesapal dashboard, scrolling to the bottom of the page and under Api Credentials, click **RESEND.**
133
+
134
+ This should resend the consumer key and secret to your email.
135
+
136
+ ### Step 3 - Install the Pesapal App
137
+
138
+ Open https://apps.shopify.com/pesapal-payments then click **Add App** then you will be directed to a page to confirm installation.
139
+
140
+ In case you have not yet logged in, you might be prompted to login using your Pesapal Business account **username** and **password**.
141
+
142
+ If you are not redirected to the application after entering the username and password it may mean your username or password was entered incorrectly of which you should double check your credentials.
143
+
144
+ Fill in the Shop Details Form with the consumer key and secret from Step 2 above and press save.
145
+
146
+ Paste your Pesapal Consumer Key and Secret
147
+
148
+ You will then be redirected back to your Shopify Store Admin Dashboard to activate the Pesapal App.
149
+
150
+ ### Step 4 - Activate the Pesapal App
151
+
152
+ Scroll to the bottom then Click 'Activate' at the bottom of the page to activate. (Ensure the "Enable Test Mode" option is NOT selected as shown in the screenshot below).
153
+
154
+ Activate The Pesapal App
155
+
156
+ ### Step 5 - Ensure customers can checkout
157
+
158
+ Visit your store website. Select an item and checkout. You should be able to see Pesapal as a payment option. Confirm the Pesapal App has been installed correctly by clicking "Pay with Pesapal". You should be directed to a Pesapal page where you can select the various payment methods supported.
159
+
160
+ ### What is Test Mode?
161
+
162
+ Test mode allows you to checkout and see how the Pesapal payment process works without using real money.
163
+
164
+ To enable the Pesapal App Test Mode, go to your Store Admin Dashboard → Settings → Payments → Pesapal -> Manage
165
+
166
+ Scroll to the bottom, Activate "test mode" and press "save"
167
+
168
+ Then go to your store/shop, add items to your cart, checkout an item and select Pesapal.
169
+
170
+ You will then see our demo payment page where you can make a "dummy payment" without using real money.
171
+
172
+ **IMPORTANT:** remember to turn off "test mode" as soon as you are done previewing the check out process. Otherwise your customers will not be able to make payments.
173
+
174
+ How to Enable Test Mode
175
+
176
+ In case you see any error, please send an email to developer@pesapal.com and kindy include screenshots so that we can assist you more efficiently.
knowledge_base/pesapal/pesapal_pos_api/pesapal_pos_api_intro.md ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Pesapal POS API
2
+
3
+ Our Pesapal Sabi machines have the ability to allow businesses / merchants receive transactional data once customers make payment using these machines.
4
+
5
+ There are 2 ways you can receive payments done on the Pesapal Sabi machines:
6
+
7
+ ## 1. Wireless Connection
8
+
9
+ This refers to a secure over the internet, HTTP POST REST (Representational State Transfer) API. You will need to share with Pesapal a secured URL (SSL enabled), to which Pesapal will post transactional data for each successful payment made via your Pesapal Sabi machine.
10
+
11
+ Click here to view the API documentation.
12
+
13
+ ## 2. Wired Connection
14
+
15
+ This refers to a connection between your computer hosting your on-premises POS and the Pesapal Devices through a cable from the PDQ or PinPad. The connection enables the transfer of data from your POS to Pesapal Devices. Pesapal Devices undertake the transactions and send back responses to your POS to undertake next steps based on the response received.
16
+
17
+ ### Access Restriction Notice (Wired Connection)
18
+
19
+ Access to Pesapal's Sabi Wired connection APIs is restricted, and availability of documentation and assets is limited.
20
+
21
+ - If you have a developer's account at developer.pesapal.com and wish to request access, kindly contact developer@pesapal.com
22
+ - If you do not currently have a developer's account at **developer.pesapal.com**, Click here to register. After registration, please get in touch with developer@pesapal.com and request for access.
knowledge_base/pesapal/pesapal_pos_api/pesapal_wired_pos_api.md ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Wired Connection
2
+
3
+ This refers to a connection between your computer hosting your on-premises POS and the Pesapal Devices through a cable from the PDQ or PinPad.
4
+
5
+ The connection enables the transfer of data from your POS to Pesapal Devices. Pesapal Devices undertake the transactions and send back responses to your POS to undertake next steps based on the response received.
6
+
7
+ ## Access Restriction Notice
8
+
9
+ **Pesapal's Sabi APIs are restricted** - access to this documentation and assets are limited.
10
+
11
+ If you're seeing this message, you are either not logged in or your profile has limited access to this section of our documentation.
12
+
13
+ - If you have access to this APIs, make sure you're logged in. Click here to login.
14
+ - If you have a developer's account at developer.pesapal.com and wish to request access, kindly contact developer@pesapal.com
15
+ - If you do not currently have a developer's account at **developer.pesapal.com**, Click here to register. After registration, please get in touch with developer@pesapal.com and request for access.
knowledge_base/pesapal/pesapal_pos_api/pesapal_wireless_pos_api.md ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Wireless Connectivity
2
+
3
+ Achieving seamless integration with your Point of Sale (POS) is possible through wireless transmission of payment details to your designated endpoint.
4
+
5
+ The initiation of this request will originate from the pqd devices, and upon a successful transaction, the relevant information will be transmitted to your specified endpoint, allowing you to efficiently settle the bill on your end.
6
+
7
+ This guide explains the process through which merchants can obtain transactional data after customers make payments on the Sabi terminal.
8
+
9
+ To facilitate the reception of payments within their external systems, merchants are required to furnish us with a secure URL featuring SSL encryption. Pesapal, in turn, will initiate an HTTP POST request to the merchant system, posting the transaction details with the specified parameters.
10
+
11
+ ## Request Parameters
12
+
13
+ | Parameter | Data Type | Description | Example |
14
+ |-----------|-----------|-------------|---------|
15
+ | first_name | String | Represent the first name of the customer | John |
16
+ | last_name | String | Represents the last name of the customer | Doe |
17
+ | phone | String | Represents the phone number of the customer if provided | 0712345678 |
18
+ | amount | Decimal | Represents the amount paid by the customer | 1.00 |
19
+ | payment_option | String | Represent the payment option used by the customer eg. Visa, MasterCard, Mpesa | Visa |
20
+ | transaction_date | datetime | Represents date and time when the transaction was done | 2019-08-14T14:41:21.4612553Z |
21
+ | currency | String | Represents the currency used during the transaction | KES, USD |
22
+ | merchant_reference | String | Represents the merchant reference in case it was entered by the merchant | 1234 |
23
+ | id | int | Represents Pesapal unique ID | 12 |
24
+ | confirmation_code | String | Represents either confirmation of payment code for mpesa and cards | QWE1234 |
25
+
26
+ ## Sample Request
27
+
28
+ ```json
29
+ {
30
+ "id": 10463,
31
+ "first_name": "joe",
32
+ "last_name": "doe",
33
+ "phone": "+254712345678",
34
+ "amount": 1.0,
35
+ "payment_option": "Visa",
36
+ "transaction_date": "2022-02-04T14:19:05.0210431Z",
37
+ "currency": "KES",
38
+ "merchant_reference": "TEST",
39
+ "confirmation_code": "test10"
40
+ }
41
+ ```
42
+
43
+ ## Sample Response
44
+
45
+ ```json
46
+ {
47
+ "status": "200",
48
+ "message": "Ok"
49
+ }
50
+ ```
51
+
52
+ **NB:** For a tailored solution corresponding to the above payload, please reach out to developer@pesapal.com. Our dedicated team is ready to provide assistance and support.
knowledge_base/sentezo/sentezo_wallet_api_docs.md ADDED
@@ -0,0 +1,639 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ssentezo Wallet Version 2.x 🤩 API Documentation
2
+
3
+ **Ssentezo Wallet** is a platform which enables businesses and developers to collect or deliver payments to the last mile through digital platforms like mobile money.
4
+
5
+ Our simple APIs make it easy to collect your payments in a few minutes. Add our web links to your website to collect payments with zero code.
6
+
7
+ ## Ssentezo Wallet Environment/MODE
8
+
9
+ The Wallet has two types of environments in which it operates:
10
+
11
+ - **LIVE**: In this environment, actual money is credited and debited from the MSISDN/phone numbers that you specify during Deposit and Withdraw operations. Real money is moved around, so be sure to have tested your application well from SANDBOX before moving into this mode.
12
+
13
+ - **SANDBOX**: This is a testing environment where no actual money is collected and disbursed. You can perform transactions just like in LIVE mode, but all these are theoretical. The MSISDN/phone numbers that you specify during Deposit and Withdraw operations will neither be credited nor debited.
14
+
15
+ In order to access **LIVE** mode, register at https://wallet.ssentezo.com/merchants/register.
16
+
17
+ In order to access **SANDBOX** mode, register at https://devwallet.ssentezo.com/merchants/register.
18
+
19
+ **NOTE**: Functionality in LIVE and SANDBOX modes is exactly the same. All that changes is the endpoint being called: `https://wallet.ssentezo.com/api` for LIVE mode and `https://devwallet.ssentezo.com/api` for SANDBOX mode.
20
+
21
+ ## Authorization
22
+
23
+ The API utilizes **Auth Basic** authorization method. This requires you to possess valid API credentials which can be generated from the API Access menu in your Wallet Account.
24
+
25
+ ## Limits
26
+
27
+ - API does not support entry of negative figures, exponential figures (e.g., e100), or characters while entering the amount to be transacted.
28
+ - Only transactions between **UGX 500 to UGX 7,000,000** are allowed, provided the current wallet balance can meet your charges as defined in your Ssentezo agreement.
29
+ - Charges are applicable to all transactions as specified in your contract for use of this service.
30
+
31
+ ## Supported Currencies
32
+
33
+ The wallet currently supports only Uganda Shillings (UGX) for transactions.
34
+
35
+ | ISO Code | Currency Name | Status |
36
+ |----------|---------------|--------|
37
+ | UGX | Uganda Shillings | Supported |
38
+
39
+ ## Setting up Authorization
40
+
41
+ Using your generated **API USER** and **API KEY**. The wallet follows a standard basic auth to secure the API where the Authorization header is sent encoded to base64.
42
+
43
+ **NOTE**: All requests must contain the authorization header.
44
+
45
+ **NOTE**: All responses are JSON-encoded strings.
46
+
47
+ **Example - How to encode the string using PHP**
48
+
49
+ ```php
50
+ // Your API user
51
+ $apiUser = '';
52
+
53
+ // Your API key
54
+ $apiKey = '';
55
+
56
+ $encodedString = base64_encode($apiUser . ':' . $apiKey);
57
+
58
+ $header = ['Authorization' => 'Basic '. $encodedString];
59
+ $header = ['Content-Type' => "application/form-data"];
60
+ ```
61
+
62
+ For other programming languages, follow the permitted syntax respectively.
63
+
64
+ ## Expected HTTP Response Codes
65
+
66
+ These are the possible response codes that can be received in the course of the transaction.
67
+
68
+ | Status Code | Interpretation |
69
+ |-------------|----------------|
70
+ | 202 | Succeeded Transaction |
71
+ | 400 | Failed Transaction |
72
+ | 401 | No Authorization Header |
73
+ | 403 | Invalid Credentials |
74
+ | 500 | An error occurred to check the message |
75
+ | 422 | Unprocessable Entity check the request body |
76
+
77
+ ## Transaction Statuses
78
+
79
+ These are the available transaction statuses that a transaction can have.
80
+
81
+ | Status | Description |
82
+ |--------|-------------|
83
+ | PENDING | Transaction still pending. All transactions begin from this state. |
84
+ | SUCCEEDED | Succeeded transaction |
85
+ | FAILED | Failed Transaction |
86
+ | INDETERMINATE | Status not yet determined. This can take up to 48hrs. |
87
+
88
+ ## API Request Responses
89
+
90
+ ### Successful Responses
91
+
92
+ For successful requests, a JSON object response is sent back containing two keys:
93
+
94
+ - **response**: This has a value of OK, indicating that the request was processed successfully.
95
+ - **data**: This is an object that holds any relevant data that should be returned as per the endpoint called.
96
+
97
+ **Example:**
98
+
99
+ ```json
100
+ {
101
+ "response": "OK",
102
+ "data": {
103
+ "amount": 1834665,
104
+ "formatted": "1,834,665",
105
+ "currency": "UGX"
106
+ }
107
+ }
108
+ ```
109
+
110
+ ### Erroneous Responses
111
+
112
+ For erroneous responses, a JSON object response is sent back containing two keys:
113
+
114
+ - **response**: This has a value of ERROR, indicating that there was an error during the course of the request.
115
+ - **error**: This is an object that holds any relevant data to the error that just occurred. It always contains a message property that describes the error and may also have other properties relevant to that error.
116
+
117
+ **Example:**
118
+
119
+ ```json
120
+ {
121
+ "response": "ERROR",
122
+ "error": {
123
+ "message": "The currency field is required."
124
+ }
125
+ }
126
+ ```
127
+
128
+ ## Checking the Account Balance
129
+
130
+ **Endpoint**: `https://wallet.ssentezo.com/api/acc_balance`
131
+
132
+ **Method**: `POST`
133
+
134
+ The endpoint above is accessed via the POST method. It does not require a request body, but a valid authorization header is mandatory.
135
+
136
+ ### Form Data Parameters
137
+
138
+ - `currency`
139
+
140
+ **Example Request**
141
+
142
+ ```json
143
+ {
144
+ "currency": "UGX"
145
+ }
146
+ ```
147
+
148
+ **Example success response**
149
+
150
+ ```json
151
+ {
152
+ "response": "OK",
153
+ "data": {
154
+ "amount": 1834665,
155
+ "formatted": "1,834,665",
156
+ "currency": "UGX"
157
+ }
158
+ }
159
+ ```
160
+
161
+ **Example error response**
162
+
163
+ ```json
164
+ {
165
+ "response": "ERROR",
166
+ "error": {
167
+ "message": "The currency field is required."
168
+ }
169
+ }
170
+ ```
171
+
172
+ ## MSISDN/Phone Number Verification
173
+
174
+ **Endpoint**: `https://wallet.ssentezo.com/api/msisdn-verification`
175
+
176
+ **Method**: `POST`
177
+
178
+ The endpoint above is accessed via the POST method.
179
+
180
+ ### Form Data Parameters
181
+
182
+ - `msisdn`
183
+
184
+ **Sample Request Body**
185
+
186
+ ```json
187
+ {
188
+ "msisdn": "256709920188"
189
+ }
190
+ ```
191
+
192
+ **Sample success response**
193
+
194
+ ```json
195
+ {
196
+ "response": "OK",
197
+ "data": {
198
+ "msisdn": "256712345678",
199
+ "FirstName": "John",
200
+ "Surname": "Doe"
201
+ }
202
+ }
203
+ ```
204
+
205
+ **Sample error response**
206
+
207
+ ```json
208
+ {
209
+ "response": "ERROR",
210
+ "error": {
211
+ "message": "The msisdn field format is invalid."
212
+ }
213
+ }
214
+ ```
215
+
216
+ ```json
217
+ {
218
+ "response": "ERROR",
219
+ "error": {
220
+ "message": "Failed to verify the name of the holder of the MSISDN"
221
+ }
222
+ }
223
+ ```
224
+
225
+ ## Withdrawing Funds from Your Wallet Account
226
+
227
+ **Endpoint**: `https://wallet.ssentezo.com/api/withdraw`
228
+
229
+ **Method**: `POST`
230
+
231
+ ### Form Data Parameters
232
+
233
+ - `externalReference`
234
+ - `msisdn`
235
+ - `amount`
236
+ - `currency`
237
+ - `reason`
238
+ - `name` (Optional)
239
+ - `success_callback` (Optional)
240
+ - `failure_callback` (Optional)
241
+
242
+ **Example request body**
243
+
244
+ ```php
245
+ // Example request body
246
+
247
+ $requestBody => [
248
+ 'externalReference' => '{your external reference}', // This should always be unique. Duplicate references will cause an error
249
+ 'msisdn' => 256770691484,
250
+ 'amount' => 2000,
251
+ 'currency' => 'UGX', // should be a valid ISO code and this currency should be supported by your wallet
252
+ 'reason' => 'Your reason for the transaction',
253
+ 'name' => '{Optional Field, You may send the name of the recipient}',
254
+ 'success_callback' => 'https://yourapp.com/my-success-callback (Optional)', // A POST endpoint the wallet will call when a transaction is successful
255
+ 'failure_callback' => 'https://yourapp.com/my-failure-callback (Optional)', // A POST endpoint the wallet will call when a transaction has failed
256
+ ];
257
+
258
+ // With PHP you can use curl, Http Request 2, or Guzzle Http to perform this request.
259
+ ```
260
+
261
+ ### Description
262
+
263
+ - **externalReference**: This is the string or number or reference that you use to refer to your transaction in your own application. It supports (250) characters. This should always be UNIQUE
264
+ - **msisdn**: This is a phone number formatted to international standard
265
+ - **amount**: The amount of money being transacted. It should not be formatted or contain any non-numeric characters. Amount should be between UGX 500 to UGX 7000000
266
+ - **currency**: Valid ISO format of the currency. Currently UGX is the only currency supported for transactions
267
+ - **reason**: Your reason for the transaction
268
+ - **name**: Name of the recipient
269
+ - **success_callback**: A POST endpoint the wallet will call when a transaction is successful
270
+ - **failure_callback**: A POST endpoint the wallet will call when a transaction has failed
271
+
272
+ **Example success response**
273
+
274
+ ```json
275
+ {
276
+ "response": "OK",
277
+ "data": {
278
+ "transactionStatus": "PENDING",
279
+ "ssentezoWalletReference": "f245ddac-1622-4dad-9a94-4e289bb6b8a4",
280
+ "financialTransactionId": "b997c60c6f445185fcd9a3a595533734b997c60c6f445185fcd9a3a595533734"
281
+ }
282
+ }
283
+ ```
284
+
285
+ ### Description
286
+
287
+ - **transactionStatus**: Status of the transaction in our system. All transactions begin from the PENDING status
288
+ - **ssentezoWalletReference**: A unique id that we use to identify the transaction in our system
289
+ - **financialTransactionId**: A unique id that identifies your transaction at the network provider eg MTN/Airtel
290
+
291
+ **NOTE**: The financialTransactionId is the transaction reference from the Network service provider
292
+
293
+ **Example error response**
294
+
295
+ ```json
296
+ {
297
+ "response": "ERROR",
298
+ "error": {
299
+ "code": "UPPER_CEILING_BREACH", // Brief overview of what the error is about
300
+ "message": "Maximum transaction amount is UGX 7,000,000" // A description of the error that occurred
301
+ }
302
+ }
303
+ ```
304
+
305
+ **Example error response (Validation)**
306
+
307
+ ```json
308
+ {
309
+ "response": "ERROR",
310
+ "error": {
311
+ "message": "The given data was invalid.",
312
+ "errors": {
313
+ "amount": ["The amount field must be at least 500."],
314
+ "msisdn": ["The msisdn field is required."],
315
+ "externalReference": [
316
+ "The external reference has already been taken."
317
+ ]
318
+ }
319
+ }
320
+ }
321
+ ```
322
+
323
+ ## Collecting Money into the Wallet (Deposit)
324
+
325
+ **Endpoint**: `https://wallet.ssentezo.com/api/deposit`
326
+
327
+ **Method**: `POST`
328
+
329
+ ### Form Data Parameters
330
+
331
+ - `externalReference`
332
+ - `msisdn`
333
+ - `amount`
334
+ - `currency`
335
+ - `reason`
336
+ - `name` (Optional)
337
+ - `success_callback` (Optional)
338
+ - `failure_callback` (Optional)
339
+
340
+ **Example - Setting up a collection request**
341
+
342
+ ```php
343
+ // Example request body
344
+
345
+ $requestBody => [
346
+ 'amount' => 1000,
347
+ 'msisdn' => 256770691484,
348
+ 'reason' => 'Your reason for the transaction',
349
+ 'currency' => 'UGX', // should be a valid ISO code and this currency should be supported by your wallet
350
+ 'name' => 'John Doe (Optional)', // A person's name you are collecting from
351
+ 'success_callback' => 'https://yourapp.com/my-success-callback (Optional)', // A POST endpoint the wallet will call when a transaction is successful
352
+ 'failure_callback' => 'https://yourapp.com/my-failure-callback (Optional)', // A POST endpoint the wallet will call when a transaction has failed
353
+ ];
354
+
355
+ // With PHP you can use curl, Http Request 2, or Guzzle Http to perform this request.
356
+ ```
357
+
358
+ **Example success response**
359
+
360
+ ```json
361
+ {
362
+ "response": "OK",
363
+ "data": {
364
+ "transactionStatus": "PENDING",
365
+ "ssentezoWalletReference": "f245ddac-1622-4dad-9a94-4e289bb6b8a4",
366
+ "financialTransactionId": "b997c60c6f445185fcd9a3a595533734b997c60c6f445185fcd9a3a595533734"
367
+ }
368
+ }
369
+ ```
370
+
371
+ **Example error response**
372
+
373
+ ```json
374
+ {
375
+ "response": "ERROR",
376
+ "error": {
377
+ "code": "UPPER_CEILING_BREACH", // Brief overview of what the error is about
378
+ "message": "Maximum transaction amount is UGX 7,000,000" // A description of the error that occurred
379
+ }
380
+ }
381
+ ```
382
+
383
+ **Example error response (Validation)**
384
+
385
+ ```json
386
+ {
387
+ "response": "ERROR",
388
+ "error": {
389
+ "message": "The given data was invalid.",
390
+ "errors": {
391
+ "amount": ["The amount field must be at least 500."],
392
+ "msisdn": ["The msisdn field is required."],
393
+ "externalReference": [
394
+ "The external reference has already been taken."
395
+ ]
396
+ }
397
+ }
398
+ }
399
+ ```
400
+
401
+ **NOTE**: A PENDING status with an HTTP status code of 202 means the transaction has been initiated and the benefactor must enter their Mobile Money Pin. Once the transaction has succeeded, the callback endpoint is then hit using a POST request to notify you.
402
+
403
+ ## Checking for a Transaction Status
404
+
405
+ **Endpoint**: `https://wallet.ssentezo.com/api/get_status/{externalReference}`
406
+
407
+ **Method**: `POST`
408
+
409
+ ### URL Parameters
410
+
411
+ - `externalReference`
412
+
413
+ **Sample success response**
414
+
415
+ ```json
416
+ {
417
+ "response": "OK",
418
+ "data": {
419
+ "transactionStatus": "SUCCEEDED",
420
+ "ssentezoWalletReference": "3181ead4-eff9-4b9b-b926-53b41e632ca5",
421
+ "externalReference": "rfg54rj59033w4672326hi45h6j6456",
422
+ "financialTransactionId": "QkK0UVtkMlCZN1ZEuKVv4NU5l2sFA1uO82fb7ef5f6a2530bc5c2118b34620b5a",
423
+ "amount": 3000,
424
+ "reason": "Sending money to someone",
425
+ "currency": "UGX",
426
+ "msisdn": "256770691484",
427
+ "transactionTime": "2024-04-24T16:24:58.000000Z"
428
+ }
429
+ }
430
+ ```
431
+
432
+ ## Receiving a Transaction Status Notification
433
+
434
+ If you would like your system to be notified immediately after a transaction status changes from the PENDING status into either SUCCEEDED or FAILED statuses, you can specify callback URLs that we shall hit to notify your system.
435
+
436
+ When making a deposit or withdraw request, as part of your request data, you can pass two parameters:
437
+
438
+ - `success_callback`
439
+ - `failure_callback`
440
+
441
+ These should contain valid URLs that will be called using a POST request to your system when a transaction either succeeds or fails. Check the documentation section for DEPOSIT or WITHDRAW for an example.
442
+
443
+ **Sample of POST data sent using the success callback**
444
+
445
+ ```json
446
+ {
447
+ "response": "OK", // Indicates a successful response
448
+ "data": {
449
+ "transactionStatus": "SUCCEEDED", // Indicates the status of the transaction (SUCCEEDED/FAILED)
450
+ "ssentezoWalletReference": "f245ddac-1622-4dad-9a94-4e289bb6b8a4",
451
+ "externalReference": "16011650463271",
452
+ "financialTransactionId": "f245ddac-1622-4dad-9a94-4e289bb6b8a4-f245ddac-1622-4dad-9a94-4e289bb6b8a4"
453
+ }
454
+ }
455
+ ```
456
+
457
+ **NOTE 1**: Please ensure that you provide valid and resolvable URLs to your system. If our system is to resolve your endpoint within 5 seconds, the request will timeout. In such cases, you will need to check the transaction status as described above.
458
+
459
+ **NOTE 2**: Please ensure that this endpoint is publicly accessible and does not require any authentication or authorization. This is because our system is decoupled from your implementation, and we rely on unrestricted callback requests to function correctly.
460
+
461
+ ## Request Bank Transfer (Push Money to the Bank)
462
+
463
+ Two Endpoints: In order to Push money to the bank, two endpoints are required.
464
+
465
+ 1. **Get list of banks** - This returns a list of all banks the we currently support. The bank id will be required in when making the actual request to transfer money to the bank.
466
+
467
+ 2. **Request bank transfer** - After you have a list of the banks, select the bank you would like to send money to, along side with other required parameters.
468
+
469
+ ### 1) Get Supported Banks
470
+
471
+ **Endpoint**: `https://wallet.ssentezo.com/api/push-to-bank/get-banks`
472
+
473
+ **Method**: `POST`
474
+
475
+ #### Form Data Parameters
476
+
477
+ Not required
478
+
479
+ **Example success response**
480
+
481
+ ```json
482
+ {
483
+ "response": "OK",
484
+ "data": {
485
+ "banks": [
486
+ {
487
+ "id": 3,
488
+ "bank_name": "Equity",
489
+ "address": "Church House",
490
+ "swift_code": "QW2456"
491
+ },
492
+ {
493
+ "id": 4,
494
+ "bank_name": "Stanbic Bank",
495
+ "address": "Church House",
496
+ "swift_code": "QW2456"
497
+ },
498
+ {
499
+ "id": 5,
500
+ "bank_name": "Housing Finance Bank",
501
+ "address": "Ntinda",
502
+ "swift_code": "QWE34E"
503
+ }
504
+ ]
505
+ }
506
+ }
507
+ ```
508
+
509
+ ### 2) Request Bank Transfer
510
+
511
+ **Endpoint**: `https://wallet.ssentezo.com/api/push-to-bank/request-bank-transfer`
512
+
513
+ **Method**: `POST`
514
+
515
+ #### Form Data Parameters
516
+
517
+ - `external_reference`
518
+ - `bank_id`
519
+ - `account_name`
520
+ - `account_number`
521
+ - `amount`
522
+ - `reason`
523
+
524
+ ```php
525
+ // Example request body
526
+
527
+ $requestBody => [
528
+ 'external_reference' => c734d323-8c1c-4160-8649-4df22443aa57, // A unique identifier that you provide to our system that will be used to identify your transaction
529
+ 'bank_id' => 3, // The id of one of the banks that we support. It's obtained from the get-banks endpoint
530
+ 'account_name' => 'John Doe', // The name of the bank account you would like the funds to be transfered to
531
+ 'account_number' => '044653389534563', // The bank account number you would like the funds to be transfered to
532
+ 'amount' => '50000', // The amount of money you would like to transfer. Minimum amount is UGX 50,000
533
+ 'reason' => 'Pushing my money to the bank', // A narration about the transaction.
534
+ ];
535
+
536
+ // With PHP you can use curl, Http Request 2, or Guzzle Http to perform this request.
537
+ ```
538
+
539
+ **Example success response**
540
+
541
+ ```json
542
+ {
543
+ "response": "OK",
544
+ "data": {
545
+ "transactionStatus": "PENDING",
546
+ "ssentezoWalletReference": "21537d54-bcd1-44a5-8614-717e093483ac",
547
+ "externalReference": "82c7d1d6-850b-4e14-a0b7-5379058b17be"
548
+ }
549
+ }
550
+ ```
551
+
552
+ **Example error response**
553
+
554
+ ```json
555
+ {
556
+ "response": "ERROR",
557
+ "error": {
558
+ "message": "Minimum transaction amount is UGX 50,000"
559
+ }
560
+ }
561
+ ```
562
+
563
+ **Example error response (Validation)**
564
+
565
+ ```json
566
+ {
567
+ "response": "ERROR",
568
+ "error": {
569
+ "message": "The given data was invalid.",
570
+ "errors": {
571
+ "bank_id": [
572
+ "The bank id field is required."
573
+ ],
574
+ "account_name": [
575
+ "The account name field is required."
576
+ ],
577
+ "account_number": [
578
+ "The account number field is required."
579
+ ],
580
+ "amount": [
581
+ "The amount field is required."
582
+ ]
583
+ }
584
+ }
585
+ }
586
+ ```
587
+
588
+ ### 3) Check Bank Transfer Status
589
+
590
+ **Endpoint**: `https://wallet.ssentezo.com/api/push-to-bank/check-bank-transfer-status`
591
+
592
+ **Method**: `POST`
593
+
594
+ #### Form Data Parameters
595
+
596
+ - `external_reference`
597
+
598
+ ```php
599
+ // Example request body
600
+
601
+ $requestBody => [
602
+ 'external_reference' => c734d323-8c1c-4160-8649-4df22443aa57, // A unique identifier that you provide to our system that will be used to identify your transaction
603
+ ];
604
+ ```
605
+
606
+ **Example success response**
607
+
608
+ ```json
609
+ {
610
+ "response": "OK",
611
+ "data": {
612
+ "transactionStatus": "SUCCEEDED",
613
+ "ssentezoWalletReference": "51c3d7b3-e70a-4016-8839-fe27a5c610fd",
614
+ "externalReference": "c734d323-8c1c-4160-8649-4df22443aa57",
615
+ "amount": 85000,
616
+ "transactionTime": "2024-09-30T12:40:48.000000Z"
617
+ }
618
+ }
619
+ ```
620
+
621
+ **Example error response (Validation)**
622
+
623
+ ```json
624
+ {
625
+ "response": "ERROR",
626
+ "error": {
627
+ "message": "The given data was invalid.",
628
+ "errors": {
629
+ "external_reference": [
630
+ "The selected external reference is invalid."
631
+ ]
632
+ }
633
+ }
634
+ }
635
+ ```
636
+
637
+ ---
638
+
639
+ That's it. Go build something great 💪🏼
ocs4dev.py CHANGED
@@ -1,946 +1,882 @@
 
 
 
 
 
 
 
 
 
1
  import os
2
- import glob
 
3
  import gradio as gr
4
  from dotenv import load_dotenv
5
- from typing import List, Dict, Any, Tuple, Optional
6
- import uuid
7
  import torch
8
- from huggingface_hub import hf_hub_download
9
- import warnings
10
  warnings.filterwarnings("ignore")
11
 
12
- # Updated imports to avoid deprecation warnings
13
- from langchain_community.document_loaders import DirectoryLoader, TextLoader
14
- from langchain_text_splitters import CharacterTextSplitter
15
- from langchain_core.documents import Document
16
- from langchain_core.messages import HumanMessage, AIMessage
17
  from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
18
- from langchain_core.runnables import RunnablePassthrough
19
  from langchain_core.output_parsers import StrOutputParser
20
-
21
- # Modern LangChain chains (replacing deprecated ConversationalRetrievalChain)
22
- from langchain.chains import create_history_aware_retriever, create_retrieval_chain
23
- from langchain.chains.combine_documents import create_stuff_documents_chain
24
 
25
  # Multi-provider LLM support
26
- from langchain_openai import OpenAIEmbeddings, ChatOpenAI
27
  from langchain_anthropic import ChatAnthropic
28
  from langchain_google_genai import ChatGoogleGenerativeAI
29
 
30
- # Supabase integration (pre-configured by admin)
31
- from langchain_community.vectorstores import SupabaseVectorStore
32
- from supabase.client import Client, create_client
33
 
34
- # Hugging Face Transformers for local Qwen model
35
- from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
36
- import accelerate
 
 
 
37
 
38
- # Configuration
39
- DEFAULT_MODEL = "Qwen/Qwen2.5-Coder-7B-Instruct-AWQ" # Quantized version for better performance
40
- TOP_K_DOCUMENTS = 5
41
 
42
- # Model configurations for each provider
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  MODEL_CONFIGS = {
44
  "openai": {
45
- "budget": "gpt-4o-mini",
46
- "premium": "o4-mini"
47
  },
48
  "anthropic": {
49
- "budget": "claude-3-5-sonnet-20241022",
50
- "premium": "claude-4-sonnet-20250109"
51
  },
52
  "google": {
53
- "budget": "gemini-2.0-flash-exp",
54
- "premium": "gemini-2.0-flash-thinking-exp-1219"
55
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  }
57
 
58
- # Load environment variables
59
- load_dotenv(override=True)
60
 
 
 
 
61
  class OCS4DevAssistant:
62
  def __init__(self):
63
- self.setup_environment()
64
- self.setup_local_model()
65
- self.setup_vector_store()
66
- self.chat_history = []
67
- self.current_provider = "local"
68
- self.current_model_tier = "budget"
69
-
70
- def setup_environment(self):
71
- """Setup environment variables - only Supabase required for vector store"""
72
- # Supabase credentials (pre-configured by admin)
73
- self.supabase_url = os.getenv('SUPABASE_URL')
74
- self.supabase_key = os.getenv('SUPABASE_SERVICE_KEY')
75
-
76
- if not self.supabase_url or not self.supabase_key:
77
- print("⚠️ Supabase not configured. Vector search will be disabled.")
78
- self.supabase_url = None
79
- self.supabase_key = None
80
-
81
- # API keys (provided by users in UI)
82
- self.openai_api_key = os.getenv('OPENAI_API_KEY')
83
- self.anthropic_api_key = os.getenv('ANTHROPIC_API_KEY')
84
- self.google_api_key = os.getenv('GOOGLE_API_KEY')
85
-
86
- def setup_local_model(self):
87
- """Initialize the local Qwen2.5-Coder model"""
88
- print("🚀 Loading Qwen2.5-Coder-7B-Instruct (AWQ quantized)...")
89
 
90
  try:
91
- # Check if CUDA is available
92
- device = "cuda" if torch.cuda.is_available() else "cpu"
93
- print(f"Using device: {device}")
94
-
95
- # Load tokenizer
96
- self.tokenizer = AutoTokenizer.from_pretrained(
97
- DEFAULT_MODEL,
98
- trust_remote_code=True
99
- )
100
-
101
- # Load quantized model
102
- self.local_model = AutoModelForCausalLM.from_pretrained(
103
- DEFAULT_MODEL,
104
- torch_dtype=torch.float16 if device == "cuda" else torch.float32,
105
- device_map="auto" if device == "cuda" else None,
106
- trust_remote_code=True,
107
- low_cpu_mem_usage=True
108
  )
109
-
110
- # Create pipeline for easier inference
111
- self.local_pipeline = pipeline(
112
- "text-generation",
113
- model=self.local_model,
114
- tokenizer=self.tokenizer,
115
- torch_dtype=torch.float16 if device == "cuda" else torch.float32,
116
- device_map="auto" if device == "cuda" else None,
117
- max_new_tokens=1024,
118
- temperature=0.3,
119
- do_sample=True,
120
- pad_token_id=self.tokenizer.eos_token_id
121
  )
122
-
123
- print("✅ Local Qwen2.5-Coder model loaded successfully!")
124
-
125
  except Exception as e:
126
- print(f"❌ Error loading local model: {e}")
127
- print("Model will be downloaded on first use...")
128
- self.local_model = None
129
- self.local_pipeline = None
130
- self.tokenizer = None
131
-
132
- def setup_vector_store(self):
133
- """Initialize vector store for retrieval only"""
134
- if not self.supabase_url or not self.supabase_key:
135
  self.vector_store = None
136
- print("⚠️ Supabase credentials not found. Vector store disabled.")
137
- print(" To enable, set SUPABASE_URL and SUPABASE_SERVICE_KEY environment variables.")
138
- return
139
 
140
- try:
141
- self.supabase_client = create_client(self.supabase_url, self.supabase_key)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
 
143
- # Test connection and check if data exists
144
- response = self.supabase_client.table("fintech_api_docs").select("count").execute()
145
- doc_count = len(response.data) if response.data else 0
146
 
147
- if doc_count == 0:
148
- print("⚠️ Supabase connected but no documents found in the database.")
149
- print(" Run the populate_supabase.py tool to add documents first.")
150
- else:
151
- print(f"✅ Supabase connected! Documents available: {doc_count}")
152
-
153
- # Use OpenAI embeddings for retrieval
154
- # Note: This requires a valid OpenAI API key for similarity search
155
- if self.openai_api_key:
156
- self.embeddings = OpenAIEmbeddings(
157
- model="text-embedding-3-small",
158
- openai_api_key=self.openai_api_key
159
- )
160
- else:
161
- print("⚠️ No OpenAI API key found. Using fallback embeddings.")
162
- print(" For best results, provide an OpenAI API key.")
163
- # Fallback: still create embeddings object but searches may not work properly
164
- self.embeddings = OpenAIEmbeddings(
165
- model="text-embedding-3-small",
166
- openai_api_key="dummy-key"
167
- )
168
 
169
- # Initialize vector store as retriever only
170
- self.vector_store = SupabaseVectorStore(
171
- client=self.supabase_client,
172
- embedding=self.embeddings,
173
- table_name="fintech_api_docs",
174
- query_name="match_documents"
 
 
 
 
 
175
  )
 
176
 
177
- print("✅ Vector store initialized as retriever!")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
 
179
  except Exception as e:
180
- print(f"❌ Vector store setup failed: {e}")
181
- print(" Ensure your Supabase table 'fintech_api_docs' exists with proper schema.")
182
- print(" Run the populate_supabase.py tool to set up the database.")
183
- self.vector_store = None
184
-
185
- def get_llm_instance(self, provider: str, tier: str, api_key: Optional[str] = None):
186
- """Get LLM instance based on provider and tier"""
187
- if provider == "local":
188
- return self.local_pipeline
189
-
190
- if not api_key:
191
- raise ValueError(f"API key required for {provider}")
 
 
192
 
193
- model_name = MODEL_CONFIGS[provider][tier]
 
 
 
 
 
 
 
194
 
195
- if provider == "openai":
196
- return ChatOpenAI(
197
- model=model_name,
198
- temperature=0.3,
199
- max_tokens=1000,
200
- openai_api_key=api_key
201
  )
202
- elif provider == "anthropic":
203
- return ChatAnthropic(
204
- model=model_name,
205
- temperature=0.3,
206
- max_tokens=1000,
207
- anthropic_api_key=api_key
 
 
208
  )
209
- elif provider == "google":
210
- return ChatGoogleGenerativeAI(
211
- model=model_name,
212
- temperature=0.3,
213
- max_output_tokens=1000,
214
- google_api_key=api_key
215
- )
216
- else:
217
- raise ValueError(f"Unsupported provider: {provider}")
218
-
219
- def generate_local_response(self, prompt: str, context: str = "") -> str:
220
- """Generate response using local Qwen model"""
221
- if not self.local_pipeline or not self.tokenizer:
222
- # Try to load model if not loaded
223
- self.setup_local_model()
224
-
225
- if not self.local_pipeline or not self.tokenizer:
226
- return "❌ Local model not available. Please use API providers or check your setup."
227
-
228
- # Format prompt for Qwen2.5-Coder
229
- system_prompt = f"""You are ocs4dev, a specialized fintech API integration assistant. You help developers integrate fintech APIs including MTN MoMo, Pesapal, and Sentezo.
230
-
231
- Your expertise includes:
232
- - API authentication and security
233
- - Code examples and implementation
234
- - Error handling and debugging
235
- - Testing and best practices
236
- - Payment gateway integration
237
-
238
- Always provide practical, code-focused responses with examples.
239
-
240
- Context: {context}"""
241
-
242
- messages = [
243
- {"role": "system", "content": system_prompt},
244
- {"role": "user", "content": prompt}
245
- ]
246
-
247
- # Apply chat template
248
- formatted_prompt = self.tokenizer.apply_chat_template(
249
- messages,
250
- tokenize=False,
251
- add_generation_prompt=True
252
- )
253
-
254
- try:
255
- # Generate response
256
- outputs = self.local_pipeline(
257
- formatted_prompt,
258
- max_new_tokens=1024,
259
  temperature=0.3,
260
  do_sample=True,
261
- pad_token_id=self.tokenizer.eos_token_id
262
  )
 
 
263
 
264
- # Extract generated text
265
- response = outputs[0]["generated_text"]
266
-
267
- # Remove the input prompt from response
268
- if formatted_prompt in response:
269
- response = response.replace(formatted_prompt, "").strip()
270
 
271
- return response
272
 
273
  except Exception as e:
274
- return f"❌ Error generating response: {str(e)}"
275
-
276
- def get_retrieval_context(self, query: str) -> str:
277
- """Get relevant context from vector store"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
278
  if not self.vector_store:
279
  return ""
280
-
281
  try:
282
- docs = self.vector_store.similarity_search(query, k=TOP_K_DOCUMENTS)
283
- context = "\n\n".join([doc.page_content for doc in docs])
284
- return context
 
 
285
  except Exception as e:
286
- print(f"Error retrieving context: {e}")
287
  return ""
288
 
289
- def create_retrieval_chain(self, llm, provider: str):
290
- """Create retrieval chain for API models"""
291
  if not self.vector_store:
292
  return None
293
-
294
- # Create retriever
295
- retriever = self.vector_store.as_retriever(
296
  search_type="similarity",
297
- search_kwargs={"k": TOP_K_DOCUMENTS}
298
  )
299
 
300
- # Contextualize question prompt
301
- contextualize_q_system_prompt = """
302
- You are ocs4dev, a fintech API integration expert. Given a chat history and the latest user question
303
- which might reference context in the chat history, formulate a standalone question
304
- which can be understood without the chat history. Focus on fintech API integration.
305
 
306
- Do NOT answer the question, just reformulate it if needed and otherwise return it as is.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
307
  """
 
 
 
 
 
 
308
 
309
- contextualize_q_prompt = ChatPromptTemplate.from_messages([
310
- ("system", contextualize_q_system_prompt),
 
 
 
 
311
  MessagesPlaceholder("chat_history"),
312
  ("human", "{input}"),
313
  ])
 
314
 
315
- # Create history-aware retriever
316
- history_aware_retriever = create_history_aware_retriever(
317
- llm, retriever, contextualize_q_prompt
318
- )
319
-
320
- # Question answering prompt
321
- qa_system_prompt = """
322
- You are ocs4dev, a specialized fintech API integration assistant. Use the following context
323
- to help developers integrate fintech APIs (MTN MoMo, Pesapal, Sentezo, etc.).
324
-
325
- Your responses should:
326
- 1. Be technically accurate and detailed
327
- 2. Include relevant code examples and snippets
328
- 3. Provide step-by-step implementation guidance
329
- 4. Include error handling best practices
330
- 5. Reference specific API endpoints and parameters
331
- 6. Suggest testing approaches
332
-
333
- Format code blocks properly with language specification for syntax highlighting.
334
- Always provide practical, actionable advice.
335
 
336
- Context: {context}
337
- """
 
 
 
338
 
 
339
  qa_prompt = ChatPromptTemplate.from_messages([
340
- ("system", qa_system_prompt),
 
 
 
 
 
 
 
 
 
 
 
341
  MessagesPlaceholder("chat_history"),
342
  ("human", "{input}"),
343
  ])
344
 
345
- # Create document chain
346
- question_answer_chain = create_stuff_documents_chain(llm, qa_prompt)
 
 
 
 
 
 
 
 
347
 
348
- # Create final RAG chain
349
- rag_chain = create_retrieval_chain(history_aware_retriever, question_answer_chain)
 
 
 
 
 
 
350
 
351
- return rag_chain
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
352
 
353
- def update_model_config(self, provider: str, tier: str, api_key: str = None):
354
- """Update current model configuration"""
355
- self.current_provider = provider
356
- self.current_model_tier = tier
357
-
358
- if provider != "local" and api_key:
359
- if provider == "openai":
360
- self.openai_api_key = api_key
361
- elif provider == "anthropic":
362
- self.anthropic_api_key = api_key
363
- elif provider == "google":
364
- self.google_api_key = api_key
365
-
366
- def chat(self, message: str, history: List[Tuple[str, str]], provider: str, tier: str, api_key: str = None) -> str:
367
- """Main chat function - returns full response (streaming handled by Gradio)"""
368
- try:
369
- # Update model configuration
370
- self.update_model_config(provider, tier, api_key)
371
 
372
- if provider == "local":
373
- # Use local model with context
374
- context = self.get_retrieval_context(message)
375
- return self.generate_local_response(message, context)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
376
  else:
377
- # Use API model
378
- api_key_map = {
379
- "openai": self.openai_api_key,
380
- "anthropic": self.anthropic_api_key,
381
- "google": self.google_api_key
382
- }
383
-
384
- current_api_key = api_key or api_key_map.get(provider)
385
- if not current_api_key:
386
- return f"❌ No API key provided for {provider}. Please enter your API key in the settings."
387
-
388
- # Get LLM instance
389
- llm = self.get_llm_instance(provider, tier, current_api_key)
390
-
391
- # Create retrieval chain
392
- rag_chain = self.create_retrieval_chain(llm, provider)
393
-
394
- if not rag_chain:
395
- # Fallback to simple context if no vector store
396
- context = self.get_retrieval_context(message)
397
- simple_prompt = f"Context: {context}\n\nQuestion: {message}\n\nProvide a detailed response about fintech API integration."
398
- return llm.invoke(simple_prompt).content
399
-
400
- # Convert Gradio history to LangChain format
401
- chat_history = []
402
- for human, assistant in history:
403
- chat_history.append(HumanMessage(content=human))
404
- chat_history.append(AIMessage(content=assistant))
405
-
406
- # Invoke RAG chain
407
- response = rag_chain.invoke({
408
- "input": message,
409
- "chat_history": chat_history
410
- })
411
-
412
- return response["answer"]
413
 
414
  except Exception as e:
415
- return f"❌ Error processing request: {str(e)}"
 
 
416
 
 
 
 
417
  def create_gradio_interface():
418
- """Create the Gradio interface optimized for HuggingFace Spaces"""
419
- print("🚀 Starting ocs4dev - Your Fintech API Integration Assistant")
420
 
421
- # Initialize assistant
422
  try:
423
  assistant = OCS4DevAssistant()
424
- print("✅ ocs4dev initialized successfully!")
425
  except Exception as e:
426
- print(f"❌ Failed to initialize ocs4dev: {e}")
427
  return None
428
 
429
- # Custom CSS for better styling and copy buttons
430
- custom_css = """
431
- .warning-box {
432
- background-color: #fff3cd;
433
- border: 1px solid #ffeaa7;
434
- border-radius: 8px;
435
- padding: 12px;
436
- margin: 10px 0;
437
- font-size: 14px;
438
- color: #856404 !important;
439
- }
440
- .model-info {
441
- background-color: #e3f2fd;
442
- border-left: 4px solid #2196f3;
443
- padding: 12px;
444
- margin: 10px 0;
445
- border-radius: 4px;
446
- color: #1565c0 !important;
447
- }
448
- .feature-box {
449
- background-color: #f8f9fa;
450
- border: 1px solid #dee2e6;
451
- border-radius: 8px;
452
- padding: 15px;
453
- margin: 10px 0;
454
- color: #212529 !important;
455
- }
456
- .code-block {
457
- background-color: #f8f9fa;
458
- border: 1px solid #e9ecef;
459
- border-radius: 6px;
460
- padding: 12px;
461
- margin: 8px 0;
462
- position: relative;
463
- font-family: 'Courier New', monospace;
464
- color: #212529 !important;
465
  }
466
- .copy-button {
467
- position: absolute;
468
- top: 8px;
469
- right: 8px;
470
- background: #007bff;
471
- color: white;
472
- border: none;
473
- padding: 4px 8px;
474
- border-radius: 4px;
475
- cursor: pointer;
476
- font-size: 12px;
477
- }
478
- .copy-button:hover {
479
- background: #0056b3;
480
- }
481
- /* Hide Gradio footer */
482
- .footer {
483
  display: none !important;
 
 
484
  }
485
 
486
- /* Overlay backdrop */
487
- .sidebar-backdrop {
488
- position: fixed;
489
- top: 0;
490
- left: 0;
491
- width: 100%;
492
- height: 100%;
493
- background: rgba(0, 0, 0, 0.5);
494
- z-index: 999;
495
- display: none;
496
- }
497
-
498
- .sidebar-backdrop.show {
499
- display: block;
500
- }
501
-
502
- /* Sidebar as overlay */
503
- .sidebar-container {
504
- position: fixed;
505
- left: 0;
506
- top: 0;
507
- height: 100vh;
508
- width: 400px; /* Wider sidebar for better text display */
509
- max-width: 90vw; /* Responsive on mobile */
510
- background: var(--background-fill-primary);
511
- border-right: 1px solid var(--border-color-primary);
512
- transform: translateX(-100%);
513
- transition: transform 0.3s ease;
514
- z-index: 1000;
515
- overflow-y: auto;
516
- overflow-x: hidden;
517
- padding: 20px;
518
- padding-top: 60px; /* Space for close button */
519
- box-sizing: border-box;
520
- box-shadow: 2px 0 10px rgba(0, 0, 0, 0.1);
521
- }
522
-
523
- .sidebar-container.open {
524
- transform: translateX(0);
525
- }
526
-
527
- /* Fix white spaces in sidebar */
528
- .sidebar-container .gr-form {
529
- gap: 0 !important;
530
- }
531
-
532
- .sidebar-container .gr-box {
533
- border: none !important;
534
- background: transparent !important;
535
- }
536
-
537
- .sidebar-container .gr-padded {
538
- padding: 8px !important;
539
- }
540
-
541
- .sidebar-container .gr-panel {
542
- background: var(--background-fill-secondary) !important;
543
- padding: 12px !important;
544
- border-radius: 8px;
545
- margin-bottom: 12px;
546
- }
547
-
548
- /* Style sidebar content */
549
- .sidebar-container h2 {
550
- color: var(--body-text-color) !important;
551
- margin-bottom: 20px;
552
- font-size: 1.5rem;
553
- }
554
-
555
- .sidebar-container .gr-markdown h2 {
556
- color: var(--body-text-color) !important;
557
- margin-top: 0;
558
- }
559
-
560
- /* Remove unwanted white borders and backgrounds */
561
- .sidebar-container .gradio-container {
562
- background: transparent !important;
563
- border: none !important;
564
- }
565
-
566
- .sidebar-container .gr-form {
567
- background: transparent !important;
568
- border: none !important;
569
- gap: 12px !important;
570
- }
571
-
572
- .sidebar-container .gr-input-wrapper {
573
- margin: 0 !important;
574
- }
575
-
576
- .sidebar-container .gr-group {
577
- background: var(--background-fill-secondary) !important;
578
- border: 1px solid var(--border-color-primary) !important;
579
- border-radius: 8px;
580
- padding: 12px;
581
- margin-bottom: 12px;
582
- }
583
-
584
- /* Ensure text visibility in sidebar */
585
- .sidebar-container * {
586
- color: var(--body-text-color) !important;
587
- }
588
 
589
- .sidebar-title {
590
- font-size: 1.5rem !important;
591
- font-weight: bold !important;
592
- margin-bottom: 20px !important;
593
- color: var(--body-text-color) !important;
594
- }
595
-
596
- .sidebar-toggle {
597
- position: fixed;
598
- left: 20px;
599
- top: 20px;
600
- z-index: 998;
601
- background: var(--button-primary-background-fill);
602
- color: var(--button-primary-text-color);
603
- border: none;
604
- padding: 12px 16px;
605
- border-radius: 8px;
606
- cursor: pointer;
607
- font-size: 18px;
608
- transition: all 0.3s ease;
609
- box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
610
- }
611
-
612
- .sidebar-toggle:hover {
613
- background: var(--button-primary-background-fill-hover);
614
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
615
- }
616
-
617
- .sidebar-close {
618
- position: absolute;
619
- right: 20px;
620
- top: 20px;
621
- background: transparent;
622
- border: none;
623
- font-size: 24px;
624
- cursor: pointer;
625
- color: var(--body-text-color);
626
- padding: 5px;
627
- border-radius: 4px;
628
- transition: background 0.2s ease;
629
- z-index: 1001;
630
- }
631
-
632
- .sidebar-close:hover {
633
  background: var(--background-fill-secondary);
 
 
 
634
  }
635
-
636
- /* Main content stays in place */
637
- .main-content {
638
- min-height: 100vh;
639
- padding-left: 70px; /* Space for menu button */
640
- }
641
-
642
- /* Custom chat styling */
643
- .chat-container {
644
- max-width: 100%;
645
- margin: 0 auto;
646
- padding: 20px;
647
- }
648
-
649
- /* Dark mode specific fixes */
650
- .dark .sidebar-container {
651
- background: var(--background-fill-primary);
652
- }
653
-
654
- .dark .warning-box {
655
- background-color: #2d2d2d;
656
- border: 1px solid #ffc107;
657
- color: #ffc107 !important;
658
- }
659
- .dark .model-info {
660
- background-color: #1a1a1a;
661
- border-left: 4px solid #64b5f6;
662
- color: #64b5f6 !important;
663
- }
664
- .dark .feature-box {
665
- background-color: #2d2d2d;
666
- border: 1px solid #495057;
667
- color: #e9ecef !important;
668
- }
669
-
670
- /* Responsive adjustments */
671
- @media (max-width: 768px) {
672
- .sidebar-container {
673
- width: 85vw;
674
- }
675
- .main-content {
676
- padding-left: 60px;
677
- }
678
  }
679
  """
680
 
681
- # Create the interface
682
- def chat_with_config(message, history, provider, tier, openai_key, anthropic_key, google_key):
683
- """Chat function with configuration"""
684
- api_key = None
685
- if provider == "openai":
686
- api_key = openai_key
687
- elif provider == "anthropic":
688
- api_key = anthropic_key
689
- elif provider == "google":
690
- api_key = google_key
691
 
692
- return assistant.chat(message, history, provider, tier, api_key)
693
-
694
- # Create interface
695
  with gr.Blocks(
696
- title="ocs4dev - Fintech API Assistant",
697
- theme=gr.themes.Soft(
698
- primary_hue="blue",
699
- secondary_hue="gray",
700
- neutral_hue="slate",
701
- ),
702
- css=custom_css,
703
  fill_height=True,
704
- js="""
705
- function() {
706
- // Add sidebar toggle functionality with backdrop
707
- const backdrop = document.createElement('div');
708
- backdrop.className = 'sidebar-backdrop';
709
- document.body.appendChild(backdrop);
710
-
711
- const toggleButton = document.createElement('button');
712
- toggleButton.innerHTML = '☰';
713
- toggleButton.className = 'sidebar-toggle';
714
- toggleButton.title = 'Open Settings';
715
-
716
- const sidebar = document.querySelector('.sidebar-container');
717
-
718
- // Add close button to sidebar
719
- const closeButton = document.createElement('button');
720
- closeButton.innerHTML = '✕';
721
- closeButton.className = 'sidebar-close';
722
- closeButton.title = 'Close Settings';
723
- sidebar.insertBefore(closeButton, sidebar.firstChild);
724
-
725
- function openSidebar() {
726
- sidebar.classList.add('open');
727
- backdrop.classList.add('show');
728
- document.body.style.overflow = 'hidden';
729
- }
730
-
731
- function closeSidebar() {
732
- sidebar.classList.remove('open');
733
- backdrop.classList.remove('show');
734
- document.body.style.overflow = '';
735
- }
736
-
737
- toggleButton.onclick = openSidebar;
738
- closeButton.onclick = closeSidebar;
739
- backdrop.onclick = closeSidebar;
740
-
741
- // ESC key to close
742
- document.addEventListener('keydown', (e) => {
743
- if (e.key === 'Escape' && sidebar.classList.contains('open')) {
744
- closeSidebar();
745
- }
746
- });
747
-
748
- document.body.appendChild(toggleButton);
749
- }
750
- """
751
  ) as interface:
752
 
753
- # Header
754
- with gr.Column(elem_classes="main-content"):
755
- gr.Markdown("# 🏦 ocs4dev - Your Fintech API Integration Assistant")
756
- gr.Markdown("*Specialized AI assistant for integrating fintech APIs including MTN MoMo, Airtel, Pesapal etc*")
757
-
758
- # Main chat interface
759
- chatbot = gr.Chatbot(
760
- height=500,
761
- placeholder="Ask me about fintech API integration, authentication, code examples, or best practices...",
762
- label="ocs4dev Assistant",
763
- show_copy_button=True, # Enable copy button for chat messages
764
- render_markdown=True,
765
- elem_classes="chat-container"
766
- )
767
-
768
- msg = gr.Textbox(
769
- placeholder="How do I authenticate with MTN MoMo API?",
770
- label="Your Question",
771
- lines=2,
772
- show_copy_button=True # Enable copy button for input
773
- )
774
-
775
- with gr.Row():
776
- clear = gr.Button("Clear", variant="secondary")
777
- submit = gr.Button("Send", variant="primary")
778
-
779
- # Example questions
780
- gr.Examples(
781
- examples=[
782
- "How do I authenticate with MTN MoMo API?",
783
- "Show me a Pesapal payment integration example",
784
- "What are the required headers for Sentezo API?",
785
- "How do I handle payment webhooks?",
786
- "Best practices for API error handling",
787
- "How to test API integrations in sandbox mode?",
788
- "Show me a complete payment flow implementation",
789
- "How to secure API keys in production?",
790
- "What's the difference between sandbox and production?",
791
- "How do I implement payment status callbacks?"
792
- ],
793
- inputs=msg,
794
- label="💡 Example Questions"
795
- )
796
-
797
- # Sidebar (hidden by default)
798
- with gr.Column(elem_classes="sidebar-container", elem_id="settings-sidebar"):
799
- gr.Markdown("## ⚙️ Configuration", elem_classes="sidebar-title")
800
 
801
- # Model provider selection
802
  with gr.Group():
 
803
  provider = gr.Radio(
804
  choices=["local", "openai", "anthropic", "google"],
805
  value="local",
806
- label="Model Provider",
807
- info="Local model is free but requires GPU. API models need keys.",
808
- elem_classes="provider-selector"
809
  )
810
-
811
  tier = gr.Radio(
812
  choices=["budget", "premium"],
813
  value="budget",
814
- label="Model Tier",
815
- info="Budget models are faster/cheaper, Premium models are more capable",
816
- elem_classes="tier-selector"
817
  )
818
 
819
- # API Keys Section
820
- with gr.Accordion("🔑 API Keys", open=True, elem_classes="api-keys-section"):
821
- gr.HTML('<div class="warning-box">⚠️ <strong>Security Warning:</strong> Create test API keys for this app and delete them after use. Never share production keys.</div>')
822
-
823
- with gr.Group():
824
- openai_key = gr.Textbox(
825
- placeholder="sk-...",
826
- label="OpenAI API Key",
827
- type="password",
828
- info="Budget: gpt-4o-mini | Premium: o4-mini (advanced reasoning)",
829
- elem_classes="api-key-input"
830
- )
831
-
832
- anthropic_key = gr.Textbox(
833
- placeholder="sk-ant-...",
834
- label="Anthropic API Key",
835
- type="password",
836
- info="Budget: claude-3.5-sonnet | Premium: claude-4-sonnet",
837
- elem_classes="api-key-input"
838
- )
839
-
840
- google_key = gr.Textbox(
841
- placeholder="AI...",
842
- label="Google API Key",
843
- type="password",
844
- info="Budget: gemini-2.0-flash | Premium: gemini-2.0-flash-thinking",
845
- elem_classes="api-key-input"
846
- )
847
-
848
- # Updated model information
849
- gr.HTML('<div class="model-info">🚀 <strong>Pro Tip:</strong> Add your API keys above for faster and better responses. Local model works but API models provide superior performance!</div>')
850
-
851
- # Features
852
- with gr.Accordion("✨ Features", open=False, elem_classes="features-section"):
853
- gr.HTML('''
854
- <div class="feature-box">
855
- <strong>🔧 Code-Focused:</strong> Optimized for API integration tasks<br>
856
- <strong>🔒 Secure:</strong> No API keys stored permanently<br>
857
- <strong>📋 Copy-Friendly:</strong> Easy code copying with built-in buttons<br>
858
- <strong>🚀 Fast:</strong> Multiple model options for best performance<br>
859
- <strong>🔄 Multi-Provider:</strong> Switch between AI models seamlessly
 
 
 
 
 
 
860
  </div>
861
- ''')
862
-
863
- # Chat functionality with simple response
864
- def respond(message, history, provider, tier, openai_key, anthropic_key, google_key):
865
- """Handle chat responses with simulated streaming"""
866
- if not message:
867
- return history, ""
868
-
869
- # Add user message to history
870
- history = history or []
871
-
872
- # Get the full response
873
- bot_message = chat_with_config(message, history, provider, tier, openai_key, anthropic_key, google_key)
874
-
875
- # Simulate streaming by yielding partial responses
876
- partial = ""
877
- words = bot_message.split(" ")
878
-
879
- # Stream words in chunks for smooth appearance
880
- chunk_size = 3 # Words per chunk
881
- for i in range(0, len(words), chunk_size):
882
- chunk = " ".join(words[i:i+chunk_size])
883
- partial += chunk + " "
884
- yield history + [(message, partial.strip())], ""
885
-
886
- # Final update with complete response
887
- yield history + [(message, bot_message)], ""
888
-
889
- # Connect the interface
890
- submit.click(
891
- respond,
892
- inputs=[msg, chatbot, provider, tier, openai_key, anthropic_key, google_key],
893
- outputs=[chatbot, msg]
894
  )
895
 
896
- msg.submit(
897
- respond,
898
- inputs=[msg, chatbot, provider, tier, openai_key, anthropic_key, google_key],
899
- outputs=[chatbot, msg]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
900
  )
901
 
902
- clear.click(lambda: ([], ""), outputs=[chatbot, msg])
903
-
904
- # Footer
905
- gr.Markdown("---")
906
- gr.Markdown("Built with ❤️ by Aaron | Using Qwen2.5-Coder, LangChain, and Gradio | [GitHub](https://github.com/aaron-official/ocs4dev.git)")
907
 
908
  return interface
909
 
910
- def populate_knowledge_base_standalone():
911
- """[DEPRECATED] Use the separate populate_supabase.py tool instead"""
912
- print("⚠️ This function is deprecated!")
913
- print(" Please use the separate 'populate_supabase.py' tool to populate the vector database.")
914
- print(" ")
915
- print(" Usage:")
916
- print(" $ python populate_supabase.py --knowledge-base ./knowledge-base")
917
- print(" ")
918
- print(" The tool will:")
919
- print(" 1. Load all markdown files from your knowledge base directory")
920
- print(" 2. Split them into chunks for better retrieval")
921
- print(" 3. Generate embeddings using OpenAI")
922
- print(" 4. Store everything in your Supabase vector database")
923
- print(" ")
924
- print(" Make sure you have set these environment variables:")
925
- print(" - SUPABASE_URL")
926
- print(" - SUPABASE_SERVICE_KEY")
927
- print(" - OPENAI_API_KEY")
928
- return False
929
 
 
 
 
930
  def main():
931
- """Main function optimized for HuggingFace Spaces"""
932
  interface = create_gradio_interface()
933
  if interface:
934
- # HuggingFace Spaces optimized launch
935
  interface.launch(
936
  server_name="0.0.0.0",
937
  server_port=7860,
938
- share=True, # Enable public URL for HF Spaces
939
- inbrowser=False, # Don't open browser in server environment
940
  show_error=True,
941
  quiet=False,
942
- max_threads=10 # Limit concurrent requests
943
  )
944
 
 
945
  if __name__ == "__main__":
946
- main()
 
 
1
+ """
2
+ ocs4dev.py — Fintech API Integration Assistant
3
+ ================================================
4
+ A RAG-powered chatbot for payment API integrations: Stripe, PayPal,
5
+ MTN MoMo, Pesapal, Sentezo, Square, Adyen & more.
6
+ Uses a local FAISS vector store (no cloud DB required) and supports
7
+ multiple LLM providers: Local Qwen, OpenAI, Anthropic, Google Gemini.
8
+ """
9
+
10
  import os
11
+ import threading
12
+ import warnings
13
  import gradio as gr
14
  from dotenv import load_dotenv
15
+ from typing import List, Tuple, Generator
16
+
17
  import torch
 
 
18
  warnings.filterwarnings("ignore")
19
 
20
+ # LangChain core (LCEL works in LangChain 1.x)
21
+ from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
 
 
 
22
  from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
 
23
  from langchain_core.output_parsers import StrOutputParser
24
+ from langchain_core.runnables import RunnablePassthrough, RunnableLambda
 
 
 
25
 
26
  # Multi-provider LLM support
27
+ from langchain_openai import ChatOpenAI
28
  from langchain_anthropic import ChatAnthropic
29
  from langchain_google_genai import ChatGoogleGenerativeAI
30
 
31
+ # Local vector store (FAISS replaces Supabase)
32
+ from langchain_community.vectorstores import FAISS
33
+ from langchain_huggingface import HuggingFaceEmbeddings
34
 
35
+ # Local model inference
36
+ from transformers import (
37
+ AutoTokenizer,
38
+ AutoModelForCausalLM,
39
+ TextIteratorStreamer,
40
+ )
41
 
42
+ # Load environment variables
43
+ load_dotenv(override=True)
 
44
 
45
+ # ─────────────────────────────────────────────────────────────────────────────
46
+ # Constants
47
+ # ─────────────────────────────────────────────────────────────────────────────
48
+ LOCAL_MODEL_ID = "Qwen/Qwen2.5-Coder-1.5B-Instruct" # ~3 GB RAM (FP16) - Best for CPU/HF Spaces
49
+ FAISS_INDEX_DIR = "./faiss_index"
50
+ EMBEDDING_MODEL = "sentence-transformers/all-MiniLM-L6-v2"
51
+ TOP_K = 5
52
+
53
+ # ─────────────────────────────────────────────────────────────────────────────
54
+ # System Prompt — the soul of ocs4dev
55
+ # ─────────────────────────────────────────────────────────────────────────────
56
+ SYSTEM_PROMPT_CORE = """\
57
+ You are **ocs4dev** — a senior-level fintech integration engineer who specialises
58
+ in payment APIs and financial infrastructure: Stripe, PayPal, Square, Adyen,
59
+ MTN MoMo, Pesapal, Sentezo (Ssentezo), Airtel Money, Flutterwave, Paystack,
60
+ Razorpay, Mollie, and related platforms worldwide.
61
+
62
+ You are NOT a generic assistant. You are the developer's *pair-programming partner*
63
+ who has shipped production payment integrations across multiple continents and
64
+ payment rails — cards, mobile money, bank transfers, crypto on-ramps, and more.
65
+
66
+ ═══════════════════════════════════════════════════════════════
67
+ CORE RULES
68
+ ═══════════════════════════════════════════════════════════════
69
+
70
+ 1. **CODE FIRST** — Every response that involves "how to" MUST include working,
71
+ copy-paste-ready code. Default to Python (requests / aiohttp), but switch to
72
+ whatever language the developer is using if you can infer it from context.
73
+ Always include:
74
+ • Full imports
75
+ • Proper error handling (try/except with specific exceptions)
76
+ • Environment variable usage for secrets (never hardcode keys)
77
+ • Comments explaining non-obvious logic
78
+ • Both success and failure response handling
79
+
80
+ 2. **ASK BEFORE YOU ASSUME** — When a developer's question is ambiguous or could
81
+ lead to multiple valid approaches, ASK 1-3 targeted follow-up questions BEFORE
82
+ diving into a full answer. Examples:
83
+ • "Are you building this for a single merchant or a marketplace with sub-accounts?"
84
+ • "Which environment — sandbox or production? The auth flow differs."
85
+ • "Do you need this to be synchronous or are you handling callbacks/webhooks?"
86
+
87
+ However, if the question is clear and specific, answer directly — don't ask
88
+ unnecessary questions just to seem thorough.
89
+
90
+ 3. **GO BEYOND THE DOCS** — If the retrieved documentation doesn't cover what the
91
+ developer needs, don't just say "this isn't supported". Instead:
92
+ • Explain clearly what the API does and doesn't support
93
+ • Propose a concrete architecture/workaround to achieve the goal
94
+ • Show code for the workaround (e.g., building a virtual account ledger
95
+ on top of a single-wallet API, or combining multiple providers)
96
+ • Flag the trade-offs and gotchas of the workaround
97
+
98
+ 4. **THINK IN SYSTEMS** — When a question implies a bigger architectural decision,
99
+ address the architecture:
100
+ • Database schema snippets when relevant
101
+ • Webhook/callback handling patterns
102
+ • Idempotency and retry strategies
103
+ • Reconciliation approaches
104
+ • Security considerations (HMAC verification, IP whitelisting, PCI compliance, etc.)
105
+
106
+ 5. **FINTECH CONTEXT** — You deeply understand:
107
+ • Multiple payment rails: cards, mobile money, bank transfers, wallets, BNPL
108
+ • Network timeouts and eventual consistency — design for both
109
+ • Sandbox vs production environments often behave differently
110
+ • Currency handling (zero-decimal currencies like UGX/JPY, 2-decimal like USD/EUR)
111
+ • Regulatory requirements (PCI-DSS, KYC/AML, SCA/3DS, transaction limits)
112
+ • Common failure modes: insufficient funds, expired tokens, declined cards,
113
+ callback URL not reachable, rate limiting, idempotency conflicts
114
+
115
+ 6. **FORMAT FOR DEVELOPERS** — Structure your responses for maximum scanability:
116
+ • Use headers (##) to separate logical sections
117
+ • Use fenced code blocks with language tags (```python, ```bash, ```json, ```sql)
118
+ • Use tables for comparing options, endpoints, or error codes
119
+ • Use bullet points for lists, numbered steps for sequences
120
+ • Bold key terms, endpoint paths, and important warnings
121
+ • Keep explanatory text concise — developers read code, not essays
122
+
123
+ 7. **BE OPINIONATED** — Don't present 5 options without a recommendation.
124
+ State your preferred approach and WHY, then mention alternatives briefly.
125
+ Example: "I'd go with webhooks over polling here because most payment
126
+ providers deliver callbacks reliably in production, and polling status
127
+ endpoints adds unnecessary load and latency."
128
+
129
+ 8. **WARN ABOUT PITFALLS** — Proactively mention common mistakes:
130
+ • ⚠️ warnings for things that will break in production
131
+ • 💡 tips for things that save debugging time
132
+ • 🔒 security notes when handling payment data
133
+ """
134
+
135
+ def build_system_prompt(context: str) -> str:
136
+ """Build the full system prompt with retrieved documentation context."""
137
+ doc_section = (
138
+ f"\n═══════════════════════════════════════════════════════════════\n"
139
+ f" RETRIEVED DOCUMENTATION\n"
140
+ f"═══════════════════════════════════════════════════════════════\n\n"
141
+ f"{context}\n\n"
142
+ f"Use the documentation above to ground your answers with specific endpoints, \n"
143
+ f"headers, and request/response formats. If the docs don't fully cover the \n"
144
+ f"developer's question, supplement with your deep knowledge of these APIs \n"
145
+ f"and clearly distinguish between doc-sourced facts and your recommendations."
146
+ ) if context else (
147
+ "\nNo specific documentation was retrieved for this query. "
148
+ "Rely on your broad knowledge of fintech APIs, but clearly state "
149
+ "when you're working from general knowledge rather than specific docs."
150
+ )
151
+ return SYSTEM_PROMPT_CORE + doc_section
152
+
153
+
154
+ def build_local_system_prompt(context: str) -> str:
155
+ """Shorter system prompt optimised for the local 1.5B model's context window."""
156
+ return (
157
+ "You are ocs4dev, a senior fintech integration engineer specializing in "
158
+ "payment APIs (Stripe, PayPal, MTN MoMo, Pesapal, Sentezo, Square, Adyen, and more).\n\n"
159
+ "Rules:\n"
160
+ "- Always include working code examples with error handling\n"
161
+ "- Ask follow-up questions when the intent is unclear\n"
162
+ "- If an API doesn't support something, propose a workaround with code\n"
163
+ "- Use Python by default, fenced code blocks with language tags\n"
164
+ "- Be concise but complete — developers read code, not essays\n"
165
+ "- Warn about common pitfalls with ⚠️\n\n"
166
+ f"Retrieved docs:\n{context if context else 'No docs retrieved — use general knowledge.'}"
167
+ )
168
+
169
+ # ─────────────────────────────────────────────────────────────────────────────
170
+ # Model configurations — March 2026
171
+ # ─────────────────────────────────────────────────────────────────────────────
172
  MODEL_CONFIGS = {
173
  "openai": {
174
+ "budget": "gpt-5-mini",
175
+ "premium": "gpt-5.2",
176
  },
177
  "anthropic": {
178
+ "budget": "claude-haiku-4-5",
179
+ "premium": "claude-opus-4-6",
180
  },
181
  "google": {
182
+ "budget": "gemini-2.0-flash",
183
+ "premium": "gemini-2.5-pro",
184
+ },
185
+ }
186
+
187
+ MODEL_DISPLAY = {
188
+ "openai": {
189
+ "budget": "GPT-5 mini",
190
+ "premium": "GPT-5.2",
191
+ },
192
+ "anthropic": {
193
+ "budget": "Claude Haiku 4.5",
194
+ "premium": "Claude Opus 4.6",
195
+ },
196
+ "google": {
197
+ "budget": "Gemini 2.0 Flash",
198
+ "premium": "Gemini 2.5 Pro",
199
+ },
200
  }
201
 
 
 
202
 
203
+ # ─────────────────────────────────────────────────────────────────────────────
204
+ # Assistant Class
205
+ # ─────────────────────────────────────────────────────────────────────────────
206
  class OCS4DevAssistant:
207
  def __init__(self):
208
+ self._setup_environment()
209
+ self._setup_vector_store()
210
+ # Local model is lazy-loaded on first use — keeps startup fast
211
+ self._local_tokenizer = None
212
+ self._local_model = None
213
+ self._model_loaded = False
214
+ self._model_loading = False
215
+ self._model_lock = threading.Lock()
216
+
217
+ # ── Environment ──────────────────────────────────────────────────────────
218
+ def _setup_environment(self):
219
+ self.openai_key = os.getenv("OPENAI_API_KEY", "")
220
+ self.anthropic_key = os.getenv("ANTHROPIC_API_KEY", "")
221
+ self.google_key = os.getenv("GOOGLE_API_KEY", "")
222
+
223
+ # ── Vector Store ─────────────────────────────────────────────────────────
224
+ def _setup_vector_store(self):
225
+ """Load local FAISS index built by build_index.py"""
226
+ if not os.path.exists(FAISS_INDEX_DIR):
227
+ print(f"⚠️ FAISS index not found at '{FAISS_INDEX_DIR}'.")
228
+ print(" Run python build_index.py to create it.")
229
+ self.vector_store = None
230
+ return
 
 
 
231
 
232
  try:
233
+ print("🔄 Loading FAISS vector index...")
234
+ self.embeddings = HuggingFaceEmbeddings(
235
+ model_name=EMBEDDING_MODEL,
236
+ model_kwargs={"device": "cpu"},
237
+ encode_kwargs={"normalize_embeddings": True},
 
 
 
 
 
 
 
 
 
 
 
 
238
  )
239
+ self.vector_store = FAISS.load_local(
240
+ FAISS_INDEX_DIR,
241
+ self.embeddings,
242
+ allow_dangerous_deserialization=True,
 
 
 
 
 
 
 
 
243
  )
244
+ print("✅ FAISS vector store loaded!")
 
 
245
  except Exception as e:
246
+ print(f"❌ Failed to load FAISS index: {e}")
 
 
 
 
 
 
 
 
247
  self.vector_store = None
 
 
 
248
 
249
+ # ── Local Model (lazy) ───────────────────────────────────────────────────
250
+ def respond(
251
+ self,
252
+ message: str,
253
+ history: list,
254
+ provider: str,
255
+ tier: str,
256
+ openai_key: str,
257
+ anthropic_key: str,
258
+ google_key: str,
259
+ ) -> Generator[str, None, None]:
260
+ """
261
+ Chat handler for gr.ChatInterface — yield plain string chunks.
262
+ ChatInterface manages history format automatically (no dict wrangling here).
263
+ """
264
+ if not message.strip():
265
+ return
266
 
267
+ # ── Get FAISS retrieval context ───────────────────────────────────────
268
+ context = self.get_context(message)
269
+ system_content = build_system_prompt(context)
270
 
271
+ # ── Local model path ─────────────────────────────────────��────────────
272
+ if provider == "local":
273
+ yield from self._local_respond(message, history, system_content)
274
+ return
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
275
 
276
+ # ── API model path ────────────────────────────────────────────────────
277
+ key_map = {
278
+ "openai": openai_key or self.openai_key,
279
+ "anthropic": anthropic_key or self.anthropic_key,
280
+ "google": google_key or self.google_key,
281
+ }
282
+ api_key = key_map.get(provider, "")
283
+ if not api_key:
284
+ yield (
285
+ f"❌ No API key for **{provider}**.\n\n"
286
+ f"Open ⚙️ **Settings → API Keys** and enter your {provider.title()} key."
287
  )
288
+ return
289
 
290
+ try:
291
+ llm = self._get_llm(provider, tier, api_key)
292
+
293
+ # Build message list: system + history + new user message
294
+ # Direct stream — avoids LCEL pipeline blocking Gradio's async loop
295
+ messages = [SystemMessage(content=system_content)]
296
+ for entry in history:
297
+ role = entry.get("role", "user") if isinstance(entry, dict) else getattr(entry, "role", "user")
298
+ content = entry.get("content") or "" if isinstance(entry, dict) else getattr(entry, "content", "")
299
+ content = str(content)
300
+ if role == "user":
301
+ messages.append(HumanMessage(content=content))
302
+ elif role == "assistant":
303
+ messages.append(AIMessage(content=content))
304
+ messages.append(HumanMessage(content=message))
305
+
306
+ response = ""
307
+ for chunk in llm.stream(messages):
308
+ response += chunk.content
309
+ yield response
310
 
311
  except Exception as e:
312
+ yield f"❌ Error calling {provider} API: {str(e)}"
313
+
314
+ def _local_respond(
315
+ self, message: str, history: list, system_content: str
316
+ ) -> Generator[str, None, None]:
317
+ """Stream response from the local Qwen model."""
318
+ if not self._load_local_model():
319
+ yield (
320
+ "⚠️ **Local model failed to load.**\n\n"
321
+ "This usually means the model weights need to be downloaded first, "
322
+ "or a quantization library (`auto-gptq`, `bitsandbytes`) is missing.\n\n"
323
+ "Try selecting **Google / OpenAI / Anthropic** instead and entering an API key."
324
+ )
325
+ return
326
 
327
+ msgs = [{"role": "system", "content": system_content}]
328
+ for entry in history[-8:]:
329
+ role = entry.get("role", "user") if isinstance(entry, dict) else getattr(entry, "role", "user")
330
+ content = entry.get("content") or "" if isinstance(entry, dict) else getattr(entry, "content", "")
331
+ content = str(content)
332
+ if role in ("user", "assistant") and content:
333
+ msgs.append({"role": role, "content": content})
334
+ msgs.append({"role": "user", "content": message})
335
 
336
+ try:
337
+ formatted = self._local_tokenizer.apply_chat_template(
338
+ msgs, tokenize=False, add_generation_prompt=True
 
 
 
339
  )
340
+ inputs = self._local_tokenizer(formatted, return_tensors="pt")
341
+ input_ids = inputs["input_ids"]
342
+ mask = inputs["attention_mask"]
343
+
344
+ streamer = TextIteratorStreamer(
345
+ self._local_tokenizer,
346
+ skip_prompt=True,
347
+ skip_special_tokens=True,
348
  )
349
+ gen_kwargs = dict(
350
+ input_ids=input_ids,
351
+ attention_mask=mask,
352
+ streamer=streamer,
353
+ max_new_tokens=16384,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
354
  temperature=0.3,
355
  do_sample=True,
356
+ pad_token_id=self._local_tokenizer.eos_token_id,
357
  )
358
+ thread = threading.Thread(target=self._local_model.generate, kwargs=gen_kwargs)
359
+ thread.start()
360
 
361
+ accumulated = ""
362
+ for token in streamer:
363
+ accumulated += token
364
+ yield accumulated
 
 
365
 
366
+ thread.join()
367
 
368
  except Exception as e:
369
+ yield f"❌ Local model error: {str(e)}"
370
+
371
+ def _load_local_model(self) -> bool:
372
+ """Attempt to load Qwen2.5-Coder-3B-Int4. Returns True on success."""
373
+ if self._model_loaded:
374
+ return True
375
+
376
+ with self._model_lock:
377
+ if self._model_loaded: # Double-checked locking
378
+ return True
379
+ if self._model_loading:
380
+ return False
381
+
382
+ self._model_loading = True
383
+ print(f"\n🚀 Loading local model: {LOCAL_MODEL_ID}...")
384
+ try:
385
+ device = "cuda" if torch.cuda.is_available() else "cpu"
386
+ print(f" Device: {device}")
387
+
388
+ self._local_tokenizer = AutoTokenizer.from_pretrained(
389
+ LOCAL_MODEL_ID,
390
+ trust_remote_code=True,
391
+ )
392
+ self._local_model = AutoModelForCausalLM.from_pretrained(
393
+ LOCAL_MODEL_ID,
394
+ dtype=torch.float16 if device == "cuda" else torch.float32,
395
+ device_map="auto" if device == "cuda" else None,
396
+ trust_remote_code=True,
397
+ low_cpu_mem_usage=True,
398
+ )
399
+ if device == "cpu":
400
+ self._local_model = self._local_model.to("cpu")
401
+
402
+ self._model_loaded = True
403
+ self._model_loading = False
404
+ print("✅ Local model loaded!")
405
+ return True
406
+
407
+ except Exception as e:
408
+ print(f"❌ Failed to load local model: {e}")
409
+ self._model_loading = False
410
+ return False
411
+
412
+ # ── Retrieval ─────────────────────────────────────────────────────────────
413
+ def get_context(self, query: str) -> str:
414
+ """Return relevant doc chunks from FAISS as a string."""
415
  if not self.vector_store:
416
  return ""
 
417
  try:
418
+ docs = self.vector_store.similarity_search(query, k=TOP_K)
419
+ return "\n\n---\n\n".join(
420
+ f"[{d.metadata.get('provider', 'Docs')}]\n{d.page_content}"
421
+ for d in docs
422
+ )
423
  except Exception as e:
424
+ print(f"Retrieval error: {e}")
425
  return ""
426
 
427
+ def get_retriever(self):
428
+ """Return a LangChain retriever from the FAISS store."""
429
  if not self.vector_store:
430
  return None
431
+ return self.vector_store.as_retriever(
 
 
432
  search_type="similarity",
433
+ search_kwargs={"k": TOP_K},
434
  )
435
 
436
+ # ── LLM Factory ──────────────────────────────────────────────────────────
437
+ def _get_llm(self, provider: str, tier: str, api_key: str):
438
+ """Return a streaming-capable LangChain LLM instance."""
439
+ model_id = MODEL_CONFIGS[provider][tier]
440
+ common = {"temperature": 0.3, "streaming": True}
441
 
442
+ if provider == "openai":
443
+ return ChatOpenAI(
444
+ model=model_id,
445
+ openai_api_key=api_key,
446
+ **common,
447
+ )
448
+ elif provider == "anthropic":
449
+ return ChatAnthropic(
450
+ model=model_id,
451
+ max_tokens=128000,
452
+ anthropic_api_key=api_key,
453
+ **common,
454
+ )
455
+ elif provider == "google":
456
+ return ChatGoogleGenerativeAI(
457
+ model=model_id,
458
+ google_api_key=api_key,
459
+ **common,
460
+ )
461
+ raise ValueError(f"Unknown provider: {provider}")
462
+
463
+ # ── RAG Chain (LCEL) ──────────────────────────────────────────────────────
464
+ def _build_rag_chain(self, llm):
465
  """
466
+ Build a history-aware RAG chain using LCEL.
467
+ Compatible with LangChain 1.x (old langchain.chains removed in v1).
468
+ """
469
+ retriever = self.get_retriever()
470
+ if not retriever:
471
+ return None
472
 
473
+ # Step 1 — reformulate the question given chat history
474
+ condense_prompt = ChatPromptTemplate.from_messages([
475
+ ("system",
476
+ "Given the chat history and the latest user question, "
477
+ "rewrite it as a clear standalone question about fintech API integration. "
478
+ "Do NOT answer — only reformulate if needed."),
479
  MessagesPlaceholder("chat_history"),
480
  ("human", "{input}"),
481
  ])
482
+ condense_chain = condense_prompt | llm | StrOutputParser()
483
 
484
+ def contextualized_retriever(inputs: dict):
485
+ """Reformulate question if history exists, then retrieve."""
486
+ if inputs.get("chat_history"):
487
+ standalone = condense_chain.invoke(inputs)
488
+ else:
489
+ standalone = inputs["input"]
490
+ return retriever.invoke(standalone)
 
 
 
 
 
 
 
 
 
 
 
 
 
491
 
492
+ def format_docs(docs) -> str:
493
+ return "\n\n---\n\n".join(
494
+ f"[{d.metadata.get('provider', 'Docs')}]\n{d.page_content}"
495
+ for d in docs
496
+ )
497
 
498
+ # Step 2 — answer using retrieved context
499
  qa_prompt = ChatPromptTemplate.from_messages([
500
+ ("system",
501
+ SYSTEM_PROMPT_CORE + """
502
+ ═══════════════════════════════════════════════════════════════
503
+ RETRIEVED DOCUMENTATION
504
+ ═══════════════════════════════════════════════════════════════
505
+
506
+ {context}
507
+
508
+ Use the documentation above to ground your answers with specific endpoints,
509
+ headers, and request/response formats. If the docs don't fully cover the
510
+ developer's question, supplement with your deep knowledge of these APIs
511
+ and clearly distinguish between doc-sourced facts and your recommendations."""),
512
  MessagesPlaceholder("chat_history"),
513
  ("human", "{input}"),
514
  ])
515
 
516
+ # LCEL chain: assign context → prompt → llm → parse
517
+ rag_chain = (
518
+ RunnablePassthrough.assign(
519
+ context=RunnableLambda(contextualized_retriever) | format_docs
520
+ )
521
+ | qa_prompt
522
+ | llm
523
+ | StrOutputParser()
524
+ )
525
+ return rag_chain
526
 
527
+ # ── Local Model Inference ─────────────────────────────────────────────────
528
+ def _generate_local_stream(
529
+ self, message: str, history: List[Tuple[str, str]]
530
+ ) -> Generator[str, None, None]:
531
+ """Stream tokens from the local Qwen model."""
532
+ if not self._load_local_model():
533
+ yield "⏳ Local model is loading, please wait a moment then try again."
534
+ return
535
 
536
+ context = self.get_context(message)
537
+ system = build_local_system_prompt(context)
538
+
539
+ # Build messages list — history is Gradio 6 dict format
540
+ messages = [{"role": "system", "content": system}]
541
+ # Include last 8 entries of history (4 turns)
542
+ for entry in history[-8:]:
543
+ role = entry.get("role", "user")
544
+ content = entry.get("content") or ""
545
+ if role in ("user", "assistant") and content:
546
+ messages.append({"role": role, "content": str(content)})
547
+ messages.append({"role": "user", "content": message})
548
+
549
+ formatted = self._local_tokenizer.apply_chat_template(
550
+ messages, tokenize=False, add_generation_prompt=True
551
+ )
552
+ inputs = self._local_tokenizer(formatted, return_tensors="pt")
553
+ input_ids = inputs["input_ids"]
554
+ mask = inputs["attention_mask"]
555
+
556
+ streamer = TextIteratorStreamer(
557
+ self._local_tokenizer,
558
+ skip_prompt=True,
559
+ skip_special_tokens=True,
560
+ )
561
 
562
+ gen_kwargs = dict(
563
+ input_ids=input_ids,
564
+ attention_mask=mask,
565
+ streamer=streamer,
566
+ max_new_tokens=16384,
567
+ temperature=0.3,
568
+ do_sample=True,
569
+ pad_token_id=self._local_tokenizer.eos_token_id,
570
+ )
 
 
 
 
 
 
 
 
 
571
 
572
+ thread = threading.Thread(
573
+ target=self._local_model.generate,
574
+ kwargs=gen_kwargs,
575
+ )
576
+ thread.start()
577
+
578
+ accumulated = ""
579
+ for token in streamer:
580
+ accumulated += token
581
+ yield accumulated
582
+
583
+ thread.join()
584
+
585
+ # ── Main Chat Dispatcher ──────────────────────────────────────────────────
586
+ def chat_stream(
587
+ self,
588
+ message: str,
589
+ history: list, # Gradio 6: [{"role": ..., "content": ...}]
590
+ provider: str,
591
+ tier: str,
592
+ openai_key: str,
593
+ anthropic_key: str,
594
+ google_key: str,
595
+ ) -> Generator:
596
+ """
597
+ Yields (updated_history, cleared_input) pairs for Gradio streaming.
598
+ All providers use FAISS for retrieval context.
599
+ """
600
+ if not message.strip():
601
+ yield history, ""
602
+ return
603
+
604
+ # ── Local model path ──────────────────────────────────────────────────
605
+ if provider == "local":
606
+ new_history = history + [(message, "")]
607
+ for partial in self._generate_local_stream(message, history):
608
+ new_history[-1] = (message, partial)
609
+ yield new_history, ""
610
+ return
611
+
612
+ # ── API model path ────────────────────────────────────────────────────
613
+ key_map = {
614
+ "openai": openai_key or self.openai_key,
615
+ "anthropic": anthropic_key or self.anthropic_key,
616
+ "google": google_key or self.google_key,
617
+ }
618
+ api_key = key_map.get(provider, "")
619
+ if not api_key:
620
+ err_msg = (
621
+ f"❌ No API key provided for **{provider}**.\n\n"
622
+ f"Open the ⚙️ **Settings** panel and enter your {provider.title()} API key."
623
+ )
624
+ yield history + [(message, err_msg)], ""
625
+ return
626
+
627
+ try:
628
+ llm = self._get_llm(provider, tier, api_key)
629
+ rag_chain = self._build_rag_chain(llm)
630
+
631
+ # Convert Gradio history → LangChain messages
632
+ lc_history = []
633
+ for h, a in history:
634
+ lc_history.append(HumanMessage(content=h))
635
+ lc_history.append(AIMessage(content=a))
636
+
637
+ partial = ""
638
+ new_history = history + [(message, "")]
639
+
640
+ if rag_chain:
641
+ for chunk in rag_chain.stream(
642
+ {"input": message, "chat_history": lc_history}
643
+ ):
644
+ # LCEL chain yields strings directly (not dicts)
645
+ if isinstance(chunk, str):
646
+ partial += chunk
647
+ elif isinstance(chunk, dict) and "answer" in chunk:
648
+ partial += chunk["answer"]
649
+ new_history[-1] = (message, partial)
650
+ yield new_history, ""
651
  else:
652
+ # Fallback: no FAISS — direct call with injected context
653
+ context = self.get_context(message)
654
+ prompt = (
655
+ f"You are ocs4dev, a fintech API expert.\n\n"
656
+ f"Context:\n{context}\n\nQuestion:\n{message}"
657
+ )
658
+ for chunk in llm.stream(prompt):
659
+ partial += chunk.content
660
+ new_history[-1] = {"role": "assistant", "content": partial}
661
+ yield new_history, ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
662
 
663
  except Exception as e:
664
+ err = append_msg(append_msg(history, "user", message), "assistant", f"❌ Error: {str(e)}")
665
+ yield err, ""
666
+
667
 
668
+ # ─────────────────────────────────────────────────────────────────────────────
669
+ # Gradio Interface
670
+ # ─────────────────────────────────────────────────────────────────────────────
671
  def create_gradio_interface():
672
+ print("🚀 Starting ocs4dev Fintech API Integration Assistant")
 
673
 
 
674
  try:
675
  assistant = OCS4DevAssistant()
676
+ print("✅ ocs4dev initialized!")
677
  except Exception as e:
678
+ print(f"❌ Initialization failed: {e}")
679
  return None
680
 
681
+ css = """
682
+ /* ── Hide ALL scrollbars ── */
683
+ *, *::before, *::after {
684
+ scrollbar-width: none !important;
685
+ -ms-overflow-style: none !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
686
  }
687
+ *::-webkit-scrollbar {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
688
  display: none !important;
689
+ width: 0 !important;
690
+ height: 0 !important;
691
  }
692
 
693
+ .footer { display: none !important; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
694
 
695
+ /* Info / warning boxes */
696
+ .info-box {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
697
  background: var(--background-fill-secondary);
698
+ border-left: 4px solid var(--color-accent);
699
+ border-radius: 4px; padding: 10px 14px;
700
+ margin: 8px 0; font-size: 13px;
701
  }
702
+ .warn-box {
703
+ background: var(--background-fill-secondary);
704
+ border-left: 4px solid #f59e0b;
705
+ border-radius: 4px; padding: 10px 14px;
706
+ margin: 8px 0; font-size: 13px;
707
+ }
708
+ .status-ok { color: #22c55e !important; font-weight: 600; }
709
+ .status-err { color: #ef4444 !important; font-weight: 600; }
710
+
711
+ /* ── Submit / Stop buttons live inside the Textbox in Gradio 6.x ── */
712
+ /* Ensure they are never clipped or collapsed */
713
+ .textbox-wrap button,
714
+ .textbox button {
715
+ display: inline-flex !important;
716
+ visibility: visible !important;
717
+ opacity: 1 !important;
718
+ flex-shrink: 0 !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
719
  }
720
  """
721
 
722
+ rag_status = (
723
+ '<span class="status-ok">✅ FAISS index loaded</span>'
724
+ if assistant.vector_store
725
+ else '<span class="status-err">⚠️ FAISS index missing — run build_index.py</span>'
726
+ )
 
 
 
 
 
727
 
 
 
 
728
  with gr.Blocks(
729
+ title="ocs4dev Fintech API Assistant",
730
+ theme=gr.themes.Soft(primary_hue="blue", secondary_hue="slate", neutral_hue="slate"),
731
+ css=css,
 
 
 
 
732
  fill_height=True,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
733
  ) as interface:
734
 
735
+ # ── Settings Sidebar ──────────────────────────────────────────────────
736
+ with gr.Sidebar(open=True):
737
+ gr.Markdown("## ⚙️ Settings")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
738
 
 
739
  with gr.Group():
740
+ gr.Markdown("### 🤖 Model Provider")
741
  provider = gr.Radio(
742
  choices=["local", "openai", "anthropic", "google"],
743
  value="local",
744
+ label="Provider",
745
+ info="Local = free (Qwen 1.5B, no key). API = cloud (needs key).",
 
746
  )
 
747
  tier = gr.Radio(
748
  choices=["budget", "premium"],
749
  value="budget",
750
+ label="Tier",
751
+ info="Budget: faster & cheaper | Premium: most capable",
 
752
  )
753
 
754
+ gr.HTML("""
755
+ <div class="info-box">
756
+ <b>Available models:</b><br>
757
+ 🔵 Google: <b>Gemini 2.0 Flash</b> / Gemini 2.5 Pro<br>
758
+ 🟢 OpenAI: <b>GPT-5 mini</b> / GPT-5.2<br>
759
+ 🟠 Anthropic: <b>Claude Haiku 4.5</b> / Claude Opus 4.6<br>
760
+ ⚫ Local: <b>Qwen2.5-Coder-1.5B-Instruct</b> (free, no key)
761
+ </div>
762
+ """)
763
+
764
+ with gr.Accordion("🔑 API Keys", open=True):
765
+ gr.HTML(
766
+ '<div class="warn-box">⚠️ <b>Security:</b> Use dev/test keys only. '
767
+ "Never paste production keys into shared UIs.</div>"
768
+ )
769
+ openai_key = gr.Textbox(
770
+ placeholder="sk-...",
771
+ label="OpenAI key",
772
+ type="password",
773
+ info="GPT-5 mini (budget) / GPT-5.2 (premium)",
774
+ )
775
+ anthropic_key = gr.Textbox(
776
+ placeholder="sk-ant-...",
777
+ label="Anthropic key",
778
+ type="password",
779
+ info="Claude Haiku 4.5 (budget) / Claude Opus 4.6 (premium)",
780
+ )
781
+ google_key = gr.Textbox(
782
+ placeholder="AIza...",
783
+ label="Google key",
784
+ type="password",
785
+ info="Gemini 2.0 Flash (budget) / Gemini 2.5 Pro (premium)",
786
+ )
787
+ gr.Markdown(
788
+ "[Get OpenAI key](https://platform.openai.com/api-keys) · "
789
+ "[Get Anthropic key](https://console.anthropic.com/) · "
790
+ "[Get Google key](https://aistudio.google.com/apikey)"
791
+ )
792
+
793
+ with gr.Accordion("📚 Knowledge Base", open=False):
794
+ gr.HTML(f"""
795
+ <div class="info-box">
796
+ {rag_status}<br><br>
797
+ 📱 <b>MTN MoMo</b> — Auth, Collections, Disbursements, Remittances<br>
798
+ 💳 <b>Pesapal</b> — E-commerce API 3.0, POS, Recurring Payments<br>
799
+ 💰 <b>Sentezo</b> — Wallet Deposits, Withdrawals, Bank Transfers<br><br>
800
+ All retrieval is local via FAISS (no cloud DB).
801
  </div>
802
+ """)
803
+
804
+ # ── Chat Interface ─────────────────────────────────────────────────────
805
+ gr.Markdown("# 🏦 ocs4dev Fintech API Integration Assistant")
806
+ gr.Markdown(
807
+ "*Your AI-powered fintech integration partner. Stripe, PayPal, MTN MoMo, Pesapal, "
808
+ "Sentezo, Square, Adyen & more. "
809
+ "Select a model in ⚙️ Settings, then start chatting.*"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
810
  )
811
 
812
+ gr.ChatInterface(
813
+ fn=assistant.respond,
814
+ additional_inputs=[provider, tier, openai_key, anthropic_key, google_key],
815
+ chatbot=gr.Chatbot(
816
+ height=480,
817
+ placeholder=(
818
+ "### 👋 Welcome to ocs4dev!\n"
819
+ "Ask me anything about payment API integration:\n"
820
+ "- Authentication, tokens & API keys\n"
821
+ "- Code examples with error handling\n"
822
+ "- Webhook / callback setup\n"
823
+ "- Subscriptions, payouts & refunds\n"
824
+ "- Error codes & debugging"
825
+ ),
826
+ label="ocs4dev",
827
+ render_markdown=True,
828
+ avatar_images=(
829
+ None,
830
+ "https://huggingface.co/front/assets/huggingface_logo-noborder.svg",
831
+ ),
832
+ ),
833
+ textbox=gr.Textbox(
834
+ placeholder="Ask about Stripe payments, MTN MoMo auth, webhook setup, virtual accounts...",
835
+ label="",
836
+ lines=2,
837
+ scale=7,
838
+ submit_btn="Send ➤",
839
+ stop_btn="Stop ■",
840
+ ),
841
+ examples=[
842
+ ["How do I create a Stripe Checkout session with error handling?"],
843
+ ["Show me MTN MoMo API authentication and access token flow"],
844
+ ["How do I verify Stripe webhook signatures in Python?"],
845
+ ["What are the required headers for Sentezo Wallet deposit API?"],
846
+ ["How do I handle 3D Secure / SCA for card payments?"],
847
+ ["Show me how to set up PayPal subscriptions with recurring billing"],
848
+ ["How do I implement idempotent payment requests?"],
849
+ ["Compare Stripe vs Pesapal for e-commerce in East Africa"],
850
+ ["How do I build a virtual account system on top of a single-wallet API?"],
851
+ ],
852
+ fill_height=True,
853
  )
854
 
855
+ gr.Markdown(
856
+ "Built with ❤️ by Aaron · "
857
+ "Qwen2.5-Coder · LangChain · FAISS · Gradio"
858
+ )
 
859
 
860
  return interface
861
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
862
 
863
+ # ─────────────────────────────────────────────────────────────────────────────
864
+ # Entry point
865
+ # ─────────────────────────────────────────────────────────────────────────────
866
  def main():
 
867
  interface = create_gradio_interface()
868
  if interface:
 
869
  interface.launch(
870
  server_name="0.0.0.0",
871
  server_port=7860,
872
+ share=True,
873
+ inbrowser=False,
874
  show_error=True,
875
  quiet=False,
876
+ max_threads=10,
877
  )
878
 
879
+
880
  if __name__ == "__main__":
881
+ main()
882
+
pyproject.toml ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [project]
2
+ name = "ocs4dev"
3
+ version = "0.1.0"
4
+ requires-python = ">=3.12"
5
+ dependencies = [
6
+ "faiss-cpu>=1.13.2",
7
+ "gradio>=6.8.0",
8
+ "langchain>=1.2.10",
9
+ "langchain-anthropic>=1.3.4",
10
+ "langchain-community>=0.4.1",
11
+ "langchain-google-genai>=4.2.1",
12
+ "langchain-huggingface>=1.2.1",
13
+ "langchain-openai>=1.1.10",
14
+ "langchain-text-splitters>=1.1.1",
15
+ "python-dotenv>=1.2.2",
16
+ "sentence-transformers>=5.2.3",
17
+ ]
requirements.txt CHANGED
@@ -1,34 +1,34 @@
1
- # Core Dependencies (IMPORTANT: transformers>=4.37.0 required for Qwen2.5-Coder)
2
- transformers==4.53.2
3
- torch==2.6.0
 
 
 
 
 
 
 
4
  accelerate==1.8.1
5
  huggingface-hub==0.33.4
6
 
7
- # LangChain (updated to avoid deprecation warnings)
8
- langchain==0.3.26
9
  langchain-community==0.3.27
10
  langchain-core==0.3.69
11
- langchain-text-splitters==0.3.8
12
 
13
- # Multi-provider LLM support
14
  langchain-openai==0.3.28
15
  langchain-anthropic==0.3.17
16
  langchain-google-genai==2.1.8
17
 
18
- # Supabase
19
- supabase==2.16.0
20
- vecs==0.4.5
21
-
22
- # UI Framework
23
- gradio==5.31.0
24
 
25
- # Utilities
26
- python-dotenv==1.1.1
27
- numpy==2.0.2
28
- sentence-transformers==4.1.0
29
 
30
- # Quantization and Performance
31
- bitsandbytes==0.46.1 # For 8-bit quantization
32
- auto-gptq==0.7.1 # For GPTQ quantization
33
- autoawq==0.2.9 # For AWQ quantization
34
- intel_extension_for_pytorch==2.7.0 # Intel optimization for PyTorch
 
1
+ # =====================================================================
2
+ # ocs4dev - Requirements
3
+ # HuggingFace Spaces compatible (CPU-optimized, no external DB needed)
4
+ # =====================================================================
5
+
6
+ # --- Core ML: CPU-only torch to avoid OOM during HF Spaces build ---
7
+ # GPU torch is ~2.5GB; CPU-only is ~250MB → prevents exit code 137
8
+ --extra-index-url https://download.pytorch.org/whl/cpu
9
+ torch==2.10.0+cpu
10
+ transformers==5.2.0
11
  accelerate==1.8.1
12
  huggingface-hub==0.33.4
13
 
14
+ # --- LangChain ---
 
15
  langchain-community==0.3.27
16
  langchain-core==0.3.69
17
+ langchain-huggingface==0.1.2
18
 
19
+ # --- Multi-provider LLM support ---
20
  langchain-openai==0.3.28
21
  langchain-anthropic==0.3.17
22
  langchain-google-genai==2.1.8
23
 
24
+ # --- Local Vector Store (replaces Supabase) ---
25
+ faiss-cpu==1.10.0
26
+ sentence-transformers==5.2.3
 
 
 
27
 
28
+ # --- UI Framework ---
29
+ gradio==6.8.0
 
 
30
 
31
+ # --- Utilities ---
32
+ python-dotenv==1.2.2
33
+ # numpy: use range to get pre-built binary wheel (avoids source compilation OOM)
34
+ numpy>=2.0
 
uv.lock ADDED
The diff for this file is too large to render. See raw diff