Spaces:
Running
Running
Commit ·
df6e544
1
Parent(s): 9958640
Major Upgrade: Gradio 6, Global Fintech Assistant, Improved prompts and token limits
Browse files- .gitattributes +1 -0
- .gitignore +10 -2
- .gradio/certificate.pem +31 -0
- README.md +45 -52
- build_index.py +181 -0
- env_example.txt +1 -5
- faiss_index/index.faiss +3 -0
- faiss_index/index.pkl +3 -0
- knowledge_base/mtn_momo/mtn_momo_api_brand_guidelines.md +11 -0
- knowledge_base/mtn_momo/mtn_momo_api_callback_docs.md +91 -0
- knowledge_base/mtn_momo/mtn_momo_api_error_codes.md +31 -0
- knowledge_base/mtn_momo/mtn_momo_api_faq.md +130 -0
- knowledge_base/mtn_momo/mtn_momo_api_getting_started.md +37 -0
- knowledge_base/mtn_momo/mtn_momo_api_intro.md +14 -0
- knowledge_base/mtn_momo/mtn_momo_api_sandbox_use_cases.md +155 -0
- knowledge_base/mtn_momo/mtn_momo_api_use_cases.md +161 -0
- knowledge_base/mtn_momo/mtn_momo_api_user_and_key_management.md +162 -0
- knowledge_base/mtn_momo/mtn_momo_error_codes.md +72 -0
- knowledge_base/mtn_momo/mtn_momo_open_apis_ sandbox_documentation.md +1358 -0
- knowledge_base/pesapal/pesapal_e-commerce-api-3.0/pesapal_e-commerce_api_3.0_auth_docs.md +67 -0
- knowledge_base/pesapal/pesapal_e-commerce-api-3.0/pesapal_e-commerce_api_3.0_e-commerce_intro.md +30 -0
- knowledge_base/pesapal/pesapal_e-commerce-api-3.0/pesapal_e-commerce_api_3.0_get_ipn_docs.md +49 -0
- knowledge_base/pesapal/pesapal_e-commerce-api-3.0/pesapal_e-commerce_api_3.0_get_transaction_status_docs.md +97 -0
- knowledge_base/pesapal/pesapal_e-commerce-api-3.0/pesapal_e-commerce_api_3.0_ipn_docs.md +88 -0
- knowledge_base/pesapal/pesapal_e-commerce-api-3.0/pesapal_e-commerce_api_3.0_order_cancellation_api_docs.md +68 -0
- knowledge_base/pesapal/pesapal_e-commerce-api-3.0/pesapal_e-commerce_api_3.0_recurring_payments_docs.md +217 -0
- knowledge_base/pesapal/pesapal_e-commerce-api-3.0/pesapal_e-commerce_api_3.0_refund_api_docs.md +78 -0
- knowledge_base/pesapal/pesapal_e-commerce-api-3.0/pesapal_e-commerce_api_3.0_submit_order_docs.md +165 -0
- knowledge_base/pesapal/pesapal_integration_guide.md +40 -0
- knowledge_base/pesapal/pesapal_plugins/pesapal_plugin_installation_guides.md +176 -0
- knowledge_base/pesapal/pesapal_pos_api/pesapal_pos_api_intro.md +22 -0
- knowledge_base/pesapal/pesapal_pos_api/pesapal_wired_pos_api.md +15 -0
- knowledge_base/pesapal/pesapal_pos_api/pesapal_wireless_pos_api.md +52 -0
- knowledge_base/sentezo/sentezo_wallet_api_docs.md +639 -0
- ocs4dev.py +744 -808
- pyproject.toml +17 -0
- requirements.txt +22 -22
- 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
|
| 13 |
|
| 14 |
-
|
| 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 |
-
|
| 19 |
-
-
|
| 20 |
-
-
|
| 21 |
-
- 📋 **
|
| 22 |
-
-
|
| 23 |
-
-
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
|
| 34 |
-
##
|
| 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 |
-
|
| 40 |
-
-
|
| 41 |
-
|
| 42 |
-
|
|
|
|
|
|
|
| 43 |
|
| 44 |
## Quick Start
|
|
|
|
| 45 |
1. Visit the [Space URL](https://huggingface.co/spaces/YOUR_USERNAME/ocs4dev)
|
| 46 |
-
2. Choose
|
| 47 |
-
3. Add API
|
| 48 |
-
4. Start asking questions
|
| 49 |
|
| 50 |
-
##
|
| 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 |
-
#
|
| 61 |
-
|
| 62 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 63 |
|
| 64 |
-
|
| 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 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
## Support
|
| 84 |
-
For issues or questions, please open an issue on GitHub or contact me.
|
| 85 |
|
| 86 |
---
|
| 87 |
-
Built with ❤️
|
|
|
|
| 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 |
-
#
|
| 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
|
|
|
|
| 3 |
import gradio as gr
|
| 4 |
from dotenv import load_dotenv
|
| 5 |
-
from typing import List,
|
| 6 |
-
|
| 7 |
import torch
|
| 8 |
-
from huggingface_hub import hf_hub_download
|
| 9 |
-
import warnings
|
| 10 |
warnings.filterwarnings("ignore")
|
| 11 |
|
| 12 |
-
#
|
| 13 |
-
from
|
| 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
|
| 27 |
from langchain_anthropic import ChatAnthropic
|
| 28 |
from langchain_google_genai import ChatGoogleGenerativeAI
|
| 29 |
|
| 30 |
-
#
|
| 31 |
-
from langchain_community.vectorstores import
|
| 32 |
-
from
|
| 33 |
|
| 34 |
-
#
|
| 35 |
-
from transformers import
|
| 36 |
-
|
|
|
|
|
|
|
|
|
|
| 37 |
|
| 38 |
-
#
|
| 39 |
-
|
| 40 |
-
TOP_K_DOCUMENTS = 5
|
| 41 |
|
| 42 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
MODEL_CONFIGS = {
|
| 44 |
"openai": {
|
| 45 |
-
"budget":
|
| 46 |
-
"premium": "
|
| 47 |
},
|
| 48 |
"anthropic": {
|
| 49 |
-
"budget":
|
| 50 |
-
"premium": "claude-4-
|
| 51 |
},
|
| 52 |
"google": {
|
| 53 |
-
"budget":
|
| 54 |
-
"premium": "gemini-2.
|
| 55 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 56 |
}
|
| 57 |
|
| 58 |
-
# Load environment variables
|
| 59 |
-
load_dotenv(override=True)
|
| 60 |
|
|
|
|
|
|
|
|
|
|
| 61 |
class OCS4DevAssistant:
|
| 62 |
def __init__(self):
|
| 63 |
-
self.
|
| 64 |
-
self.
|
| 65 |
-
|
| 66 |
-
self.
|
| 67 |
-
self.
|
| 68 |
-
self.
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
self.
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 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 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 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 |
-
|
| 111 |
-
|
| 112 |
-
|
| 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"❌
|
| 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 |
-
|
| 141 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 142 |
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 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 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 175 |
)
|
|
|
|
| 176 |
|
| 177 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 178 |
|
| 179 |
except Exception as e:
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
|
|
|
|
|
|
| 192 |
|
| 193 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 194 |
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
temperature=0.3,
|
| 199 |
-
max_tokens=1000,
|
| 200 |
-
openai_api_key=api_key
|
| 201 |
)
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
|
|
|
|
|
|
| 208 |
)
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 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.
|
| 262 |
)
|
|
|
|
|
|
|
| 263 |
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
|
| 268 |
-
if formatted_prompt in response:
|
| 269 |
-
response = response.replace(formatted_prompt, "").strip()
|
| 270 |
|
| 271 |
-
|
| 272 |
|
| 273 |
except Exception as e:
|
| 274 |
-
|
| 275 |
-
|
| 276 |
-
def
|
| 277 |
-
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 278 |
if not self.vector_store:
|
| 279 |
return ""
|
| 280 |
-
|
| 281 |
try:
|
| 282 |
-
docs = self.vector_store.similarity_search(query, k=
|
| 283 |
-
|
| 284 |
-
|
|
|
|
|
|
|
| 285 |
except Exception as e:
|
| 286 |
-
print(f"
|
| 287 |
return ""
|
| 288 |
|
| 289 |
-
def
|
| 290 |
-
"""
|
| 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":
|
| 298 |
)
|
| 299 |
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
|
| 306 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 307 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 308 |
|
| 309 |
-
|
| 310 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 311 |
MessagesPlaceholder("chat_history"),
|
| 312 |
("human", "{input}"),
|
| 313 |
])
|
|
|
|
| 314 |
|
| 315 |
-
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
|
| 319 |
-
|
| 320 |
-
|
| 321 |
-
|
| 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 |
-
|
| 337 |
-
|
|
|
|
|
|
|
|
|
|
| 338 |
|
|
|
|
| 339 |
qa_prompt = ChatPromptTemplate.from_messages([
|
| 340 |
-
("system",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 341 |
MessagesPlaceholder("chat_history"),
|
| 342 |
("human", "{input}"),
|
| 343 |
])
|
| 344 |
|
| 345 |
-
#
|
| 346 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 347 |
|
| 348 |
-
|
| 349 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 350 |
|
| 351 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 352 |
|
| 353 |
-
|
| 354 |
-
|
| 355 |
-
|
| 356 |
-
|
| 357 |
-
|
| 358 |
-
|
| 359 |
-
|
| 360 |
-
|
| 361 |
-
|
| 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 |
-
|
| 373 |
-
|
| 374 |
-
|
| 375 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 376 |
else:
|
| 377 |
-
#
|
| 378 |
-
|
| 379 |
-
|
| 380 |
-
"
|
| 381 |
-
"
|
| 382 |
-
|
| 383 |
-
|
| 384 |
-
|
| 385 |
-
|
| 386 |
-
|
| 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 |
-
|
|
|
|
|
|
|
| 416 |
|
|
|
|
|
|
|
|
|
|
| 417 |
def create_gradio_interface():
|
| 418 |
-
"
|
| 419 |
-
print("🚀 Starting ocs4dev - Your Fintech API Integration Assistant")
|
| 420 |
|
| 421 |
-
# Initialize assistant
|
| 422 |
try:
|
| 423 |
assistant = OCS4DevAssistant()
|
| 424 |
-
print("✅ ocs4dev initialized
|
| 425 |
except Exception as e:
|
| 426 |
-
print(f"❌
|
| 427 |
return None
|
| 428 |
|
| 429 |
-
|
| 430 |
-
|
| 431 |
-
|
| 432 |
-
|
| 433 |
-
|
| 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 |
-
|
| 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 |
-
|
| 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 |
-
|
| 590 |
-
|
| 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 |
-
|
| 637 |
-
|
| 638 |
-
|
| 639 |
-
|
| 640 |
-
}
|
| 641 |
-
|
| 642 |
-
|
| 643 |
-
|
| 644 |
-
|
| 645 |
-
|
| 646 |
-
|
| 647 |
-
|
| 648 |
-
|
| 649 |
-
|
| 650 |
-
|
| 651 |
-
|
| 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 |
-
|
| 682 |
-
|
| 683 |
-
|
| 684 |
-
|
| 685 |
-
|
| 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
|
| 697 |
-
theme=gr.themes.Soft(
|
| 698 |
-
|
| 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 |
-
#
|
| 754 |
-
with gr.
|
| 755 |
-
gr.Markdown("#
|
| 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="
|
| 807 |
-
info="Local
|
| 808 |
-
elem_classes="provider-selector"
|
| 809 |
)
|
| 810 |
-
|
| 811 |
tier = gr.Radio(
|
| 812 |
choices=["budget", "premium"],
|
| 813 |
value="budget",
|
| 814 |
-
label="
|
| 815 |
-
info="Budget
|
| 816 |
-
elem_classes="tier-selector"
|
| 817 |
)
|
| 818 |
|
| 819 |
-
|
| 820 |
-
|
| 821 |
-
|
| 822 |
-
|
| 823 |
-
|
| 824 |
-
|
| 825 |
-
|
| 826 |
-
|
| 827 |
-
|
| 828 |
-
|
| 829 |
-
|
| 830 |
-
|
| 831 |
-
|
| 832 |
-
|
| 833 |
-
|
| 834 |
-
|
| 835 |
-
|
| 836 |
-
|
| 837 |
-
|
| 838 |
-
)
|
| 839 |
-
|
| 840 |
-
|
| 841 |
-
|
| 842 |
-
|
| 843 |
-
|
| 844 |
-
|
| 845 |
-
|
| 846 |
-
|
| 847 |
-
|
| 848 |
-
|
| 849 |
-
|
| 850 |
-
|
| 851 |
-
|
| 852 |
-
|
| 853 |
-
|
| 854 |
-
|
| 855 |
-
|
| 856 |
-
|
| 857 |
-
|
| 858 |
-
|
| 859 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 860 |
</div>
|
| 861 |
-
|
| 862 |
-
|
| 863 |
-
# Chat
|
| 864 |
-
|
| 865 |
-
|
| 866 |
-
|
| 867 |
-
|
| 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 |
-
|
| 897 |
-
respond,
|
| 898 |
-
|
| 899 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 900 |
)
|
| 901 |
|
| 902 |
-
|
| 903 |
-
|
| 904 |
-
|
| 905 |
-
|
| 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,
|
| 939 |
-
inbrowser=False,
|
| 940 |
show_error=True,
|
| 941 |
quiet=False,
|
| 942 |
-
max_threads=10
|
| 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 |
-
#
|
| 2 |
-
|
| 3 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
accelerate==1.8.1
|
| 5 |
huggingface-hub==0.33.4
|
| 6 |
|
| 7 |
-
# LangChain
|
| 8 |
-
langchain==0.3.26
|
| 9 |
langchain-community==0.3.27
|
| 10 |
langchain-core==0.3.69
|
| 11 |
-
langchain-
|
| 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 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
# UI Framework
|
| 23 |
-
gradio==5.31.0
|
| 24 |
|
| 25 |
-
#
|
| 26 |
-
|
| 27 |
-
numpy==2.0.2
|
| 28 |
-
sentence-transformers==4.1.0
|
| 29 |
|
| 30 |
-
#
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 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
|
|
|