Skip to content

Integration Work

YeboLearn's integration ecosystem connects students, teachers, and parents with essential services. This document tracks active integration development and third-party service work.

Last Updated: November 22, 2025

Active Integrations

1. M-Pesa Payment Integration (Sprint 26 - In Progress)

Status: 40% Complete, On Track for Nov 29 Launch

Business Value:

  • Primary payment method in Lesotho and Southern Africa
  • Unlock $5/month subscriptions for students
  • Reduce payment friction (no credit card needed)
  • Enable mobile-first payments
  • Market expansion opportunity

Integration Architecture:

YeboLearn Platform
├─ Student initiates payment
│   └─ Selects M-Pesa, enters phone number
├─ Backend creates payment request
│   ├─ Generates transaction ID
│   ├─ Calls M-Pesa API (STK Push)
│   └─ Stores pending transaction
├─ M-Pesa sends push to phone
│   └─ Student enters PIN
├─ M-Pesa processes payment
│   └─ Sends webhook callback
├─ YeboLearn receives callback
│   ├─ Verifies signature
│   ├─ Updates transaction status
│   ├─ Grants course access
│   └─ Sends confirmation email/SMS
└─ Student accesses paid content

Technical Implementation:

typescript
// M-Pesa STK Push (Payment Initiation)
export async function initiatePayment(
  phoneNumber: string,
  amount: number,
  courseId: string,
  userId: string
): Promise<PaymentResponse> {
  // Generate transaction ID
  const transactionId = generateTransactionId();

  // M-Pesa API credentials
  const { consumerKey, consumerSecret, shortCode, passkey } = mpesaConfig;

  // Get OAuth token
  const token = await getOAuthToken(consumerKey, consumerSecret);

  // Prepare STK Push request
  const timestamp = generateTimestamp();
  const password = generatePassword(shortCode, passkey, timestamp);

  const response = await mpesaClient.stkPush({
    BusinessShortCode: shortCode,
    Password: password,
    Timestamp: timestamp,
    TransactionType: 'CustomerPayBillOnline',
    Amount: amount,
    PartyA: phoneNumber, // Customer phone
    PartyB: shortCode, // Business shortcode
    PhoneNumber: phoneNumber,
    CallBackURL: `${API_URL}/webhooks/mpesa/callback`,
    AccountReference: courseId,
    TransactionDesc: `YeboLearn Course Payment`,
  });

  // Store pending transaction
  await db.payment.create({
    data: {
      id: transactionId,
      userId,
      courseId,
      amount,
      currency: 'LSL', // Lesotho Loti
      provider: 'mpesa',
      status: 'pending',
      phoneNumber,
      mpesaCheckoutRequestId: response.CheckoutRequestID,
      createdAt: new Date(),
    },
  });

  return {
    transactionId,
    checkoutRequestId: response.CheckoutRequestID,
    message: 'Check your phone to complete payment',
  };
}

// M-Pesa Webhook Handler
export async function handleMpesaCallback(
  callbackData: MpesaCallback
): Promise<void> {
  // Verify callback signature
  if (!verifySignature(callbackData)) {
    throw new Error('Invalid callback signature');
  }

  const { Body } = callbackData;
  const { ResultCode, ResultDesc, CheckoutRequestID } = Body.stkCallback;

  // Find transaction
  const payment = await db.payment.findUnique({
    where: { mpesaCheckoutRequestId: CheckoutRequestID },
  });

  if (!payment) {
    logger.error('Payment not found for checkout request', { CheckoutRequestID });
    return;
  }

  // Update transaction status
  if (ResultCode === 0) {
    // Success
    await db.payment.update({
      where: { id: payment.id },
      data: {
        status: 'completed',
        mpesaReceiptNumber: Body.stkCallback.CallbackMetadata.Item.find(
          i => i.Name === 'MpesaReceiptNumber'
        )?.Value,
        completedAt: new Date(),
      },
    });

    // Grant course access
    await grantCourseAccess(payment.userId, payment.courseId);

    // Send confirmation
    await sendPaymentConfirmation(payment);

    // Emit event for analytics
    analytics.track('payment_completed', {
      userId: payment.userId,
      amount: payment.amount,
      provider: 'mpesa',
    });
  } else {
    // Failed
    await db.payment.update({
      where: { id: payment.id },
      data: {
        status: 'failed',
        errorMessage: ResultDesc,
        failedAt: new Date(),
      },
    });

    // Notify user
    await sendPaymentFailureNotification(payment, ResultDesc);
  }
}

Current Progress:

Completed (4 points):

  • ✅ M-Pesa developer account and sandbox setup
  • ✅ OAuth token generation and caching
  • ✅ Payment initiation (STK Push) endpoint
  • ✅ Basic error handling

In Progress (5 points):

  • 🚧 Webhook callback handling (50% done)
    • Signature verification complete
    • Database updates in progress
    • Testing callback scenarios

Not Started (4 points):

  • ⏳ Error recovery and retry logic
    • Handle timeout scenarios
    • Retry failed callbacks
    • Manual reconciliation
  • ⏳ Payment status dashboard
    • Admin view of all transactions
    • Filter and search
    • Export capabilities

Testing Strategy:

typescript
// Integration tests with M-Pesa sandbox
describe('M-Pesa Integration', () => {
  it('should initiate payment successfully', async () => {
    const response = await initiatePayment(
      '+26878422613', // Test number
      500, // LSL 500 (~$25)
      'course-123',
      'user-456'
    );

    expect(response.checkoutRequestId).toBeDefined();
    expect(response.message).toContain('Check your phone');
  });

  it('should handle successful payment callback', async () => {
    const callback = mockSuccessfulCallback();
    await handleMpesaCallback(callback);

    const payment = await db.payment.findUnique({
      where: { id: 'txn-123' },
    });

    expect(payment.status).toBe('completed');

    const enrollment = await db.enrollment.findFirst({
      where: {
        userId: 'user-456',
        courseId: 'course-123',
      },
    });

    expect(enrollment).toBeDefined();
  });

  it('should handle failed payment callback', async () => {
    const callback = mockFailedCallback('Insufficient funds');
    await handleMpesaCallback(callback);

    const payment = await db.payment.findUnique({
      where: { id: 'txn-123' },
    });

    expect(payment.status).toBe('failed');
    expect(payment.errorMessage).toContain('Insufficient funds');
  });
});

Beta Launch Plan:

Week 1 (Nov 29 - Dec 5): Limited Beta
- 10 test users with real accounts
- Small amounts (LSL 50-100)
- Monitor closely for issues
- Collect feedback

Week 2 (Dec 6-12): Expanded Beta
- 50 users
- Full subscription prices (LSL 500/month)
- Performance monitoring
- Support team trained

Week 3+ (Dec 13+): General Availability
- All users can pay via M-Pesa
- Marketing campaign launches
- Monitor payment success rate (target: >95%)

Risk Mitigation:

Risk: Production credentials delayed
Mitigation: Can launch beta on sandbox, switch to prod later

Risk: Payment failures due to network
Mitigation: Retry logic, manual reconciliation dashboard

Risk: Webhook not received
Mitigation: Polling backup (check status every 30s for 5 min)

Risk: Duplicate payments
Mitigation: Idempotency keys (Sprint 26 technical debt work)

2. WhatsApp Business API Integration (Sprint 27 - Planned)

Status: Planning Phase, API Access Requested

Business Value:

  • Reach students where they are (WhatsApp ubiquitous in Africa)
  • Notification delivery (better than email)
  • Two-way communication (support, reminders)
  • Course enrollment via WhatsApp
  • Low-bandwidth option for rural students

Use Cases:

1. Notifications

Quiz Reminder:
"Hi Sarah! 👋 You have a quiz due tomorrow in Mathematics 101.
Complete it before 11:59 PM to stay on track. Good luck! 📚"

Grade Available:
"Your essay on Climate Change has been graded! 🎉
Score: 85% with detailed AI feedback. View your results: [link]"

Course Enrollment:
"Welcome to Biology 201! 🧬 Your course starts Monday.
Access materials: [link]
Need help? Reply to this message."

2. Interactive Bot

Student: "What's my progress in Math?"
Bot: "You're 65% through Math 101!
- Completed: 13/20 quizzes
- Average score: 78%
- Next topic: Quadratic Equations
Keep it up! 💪"

Student: "When is my next quiz due?"
Bot: "Your next quiz is Chemistry Chapter 5, due Friday 5 PM.
Would you like to start it now? (Yes/No)"

Student: "Yes"
Bot: "Opening quiz... [link to quiz]"

Technical Architecture:

WhatsApp Cloud API
├─ YeboLearn Backend
│   ├─ Webhook receiver
│   ├─ Message handler
│   ├─ Business logic router
│   └─ Response generator
├─ Message Templates
│   ├─ Quiz reminders
│   ├─ Grade notifications
│   ├─ Course updates
│   └─ Payment confirmations
└─ Interactive Features
    ├─ Quick replies
    ├─ List messages
    ├─ Button messages
    └─ Rich media (images, docs)

Implementation Plan (Sprint 27-28):

Sprint 27 (8 points):

- [ ] WhatsApp Business API setup and verification
- [ ] Webhook endpoint for incoming messages
- [ ] Notification templates (quiz, grades, enrollment)
- [ ] Send notification implementation
- [ ] Basic testing

Features:
- One-way notifications only
- No interactive bot yet
- Manual trigger for testing

Sprint 28 (13 points):

- [ ] Interactive bot logic
- [ ] Natural language understanding (NLU)
- [ ] Command parsing ("my progress", "next quiz")
- [ ] Integration with student data
- [ ] Full testing and beta launch

Features:
- Two-way conversations
- Student queries
- Course information
- Progress tracking

Message Templates:

typescript
// WhatsApp notification templates
export const templates = {
  quizReminder: {
    name: 'quiz_reminder',
    language: 'en',
    components: [
      {
        type: 'body',
        parameters: [
          { type: 'text', text: '{{studentName}}' },
          { type: 'text', text: '{{quizName}}' },
          { type: 'text', text: '{{dueDate}}' },
        ],
      },
    ],
  },

  gradeAvailable: {
    name: 'grade_available',
    language: 'en',
    components: [
      {
        type: 'body',
        parameters: [
          { type: 'text', text: '{{assignmentName}}' },
          { type: 'text', text: '{{score}}' },
        ],
      },
      {
        type: 'button',
        sub_type: 'url',
        index: '0',
        parameters: [
          { type: 'text', text: '{{viewResultsUrl}}' },
        ],
      },
    ],
  },
};

// Send notification
async function sendWhatsAppNotification(
  phoneNumber: string,
  template: string,
  parameters: any[]
) {
  await whatsappClient.sendMessage({
    messaging_product: 'whatsapp',
    to: phoneNumber,
    type: 'template',
    template: {
      name: template,
      language: { code: 'en' },
      components: [
        {
          type: 'body',
          parameters,
        },
      ],
    },
  });
}

Compliance and Privacy:

WhatsApp Business Policy Compliance:
✓ No spam (only opt-in users)
✓ No promotional content (education only)
✓ Clear opt-out mechanism
✓ Privacy policy updated
✓ Data protection (no sensitive info in messages)

User Opt-In Flow:
1. Student enables WhatsApp notifications in settings
2. Verify phone number (SMS code)
3. Send welcome message
4. Confirm preferences (quizzes, grades, courses)
5. Allow granular control (turn off specific types)

Opt-Out Flow:
1. Reply "STOP" to any message
2. Or disable in settings
3. Immediate effect (within 5 minutes)

3. Google Gemini AI (Ongoing)

Status: Production, Continuous Optimization

Current Usage:

Active Features:
✓ Quiz generation (Gemini 1.5 Flash)
✓ Essay grading (Gemini 1.5 Pro)
✓ Content recommendations (Gemini 1.5 Flash)
✓ Study planner (Gemini 1.5 Pro) - Sprint 27

Usage Stats (November 2025):
- API calls: ~12,000/day
- Input tokens: 125M/month
- Output tokens: 48M/month
- Cost: ~$200/month
- Error rate: 1.2%

See AI Features Documentation for detailed Gemini integration work.


4. Email Service (SendGrid) (Production)

Status: Stable, Minor Improvements Planned

Current Integration:

typescript
// Transactional emails
export async function sendEmail(
  to: string,
  template: string,
  data: any
) {
  await sendGridClient.send({
    to,
    from: '[email protected]',
    templateId: templates[template],
    dynamicTemplateData: data,
  });
}

// Email templates
const emailTemplates = {
  welcome: 'd-123456',
  quizReminder: 'd-234567',
  gradeAvailable: 'd-345678',
  paymentConfirmation: 'd-456789',
  passwordReset: 'd-567890',
};

// Usage
await sendEmail(
  '[email protected]',
  'gradeAvailable',
  {
    studentName: 'Sarah',
    assignmentName: 'Climate Change Essay',
    score: 85,
    viewUrl: 'https://yebolearn.app/grades/123',
  }
);

Email Performance:

Delivery Rate: 98.5% (excellent)
Open Rate: 42% (above average)
Click Rate: 18% (good)
Bounce Rate: 1.2% (low)
Spam Reports: 0.1% (very low)

Volume:
- Daily: ~2,500 emails
- Monthly: ~75,000 emails
- Cost: $45/month

Improvements Planned (Sprint 28):

- [ ] Email preference center (granular control)
- [ ] Unsubscribe management (better UX)
- [ ] A/B testing framework
- [ ] Email analytics dashboard
- [ ] Localization (Sesotho translations)

Effort: 3 story points

5. SMS Service (Twilio) (Production)

Status: Stable, Low Usage

Current Usage:

Use Cases:
✓ Phone verification (signup, phone change)
✓ Password reset (2FA)
✓ Critical notifications (payment failures)

Volume:
- Daily: ~50 SMS
- Monthly: ~1,500 SMS
- Cost: $30/month

Delivery Rate: 99.2%

Implementation:

typescript
// Send SMS
export async function sendSMS(
  phoneNumber: string,
  message: string
) {
  await twilioClient.messages.create({
    to: phoneNumber,
    from: '+26612345678', // Twilio number
    body: message,
  });
}

// Use cases
await sendSMS(
  '+26878422613',
  'Your YeboLearn verification code is: 123456'
);

await sendSMS(
  '+26878422613',
  'Payment failed: Insufficient funds. Please try again.'
);

Future Enhancement:

Replace Twilio SMS with WhatsApp (Sprint 28+)
- Lower cost (WhatsApp: $0.005 vs SMS: $0.02)
- Better delivery (WhatsApp more reliable)
- Richer content (buttons, links)
- Two-way communication

6. Google Cloud Storage (Production)

Status: Stable, Minor Optimizations

Current Usage:

Storage Buckets:
├─ yebolearn-uploads (user content)
│   ├─ Essay submissions
│   ├─ Profile pictures
│   └─ Course materials
├─ yebolearn-assets (static assets)
│   ├─ Images, videos
│   ├─ PDFs, documents
│   └─ Thumbnails
└─ yebolearn-backups (database backups)
    └─ Daily automated backups

Storage Size: 45 GB
Monthly Cost: $9

Optimization (Sprint 27):

Current: All files in single bucket
Problem: No CDN, slow for international users

Improvement: Cloud CDN integration
- Cache static assets at edge locations
- Faster delivery (200ms → 50ms in South Africa)
- Lower egress costs
- Better user experience

Effort: 2 story points
Cost: +$5/month (offset by egress savings)

API Improvements

Public API (Beta - Sprint 28)

Status: Planning Phase

Business Value:

  • Partner integrations (schools, LMS)
  • Third-party app ecosystem
  • Data export for students
  • Platform extensibility

Planned Endpoints:

Authentication:
POST /api/v1/auth/token - OAuth2 token

Courses:
GET /api/v1/courses - List courses
GET /api/v1/courses/:id - Course details
POST /api/v1/enrollments - Enroll student

Progress:
GET /api/v1/students/:id/progress - Student progress
GET /api/v1/students/:id/grades - Grades

Quizzes:
GET /api/v1/quizzes/:id - Quiz details
POST /api/v1/quiz-attempts - Submit quiz

AI Features:
POST /api/v1/ai/generate-quiz - Generate quiz (rate limited)
POST /api/v1/ai/grade-essay - Grade essay (rate limited)

API Design:

typescript
// RESTful, JSON API
GET /api/v1/courses?page=1&limit=20

Response:
{
  "data": [
    {
      "id": "course-123",
      "title": "Mathematics 101",
      "description": "Introduction to algebra",
      "thumbnail": "https://cdn.yebolearn.app/...",
      "instructor": {
        "id": "teacher-456",
        "name": "Mr. Johnson"
      },
      "enrollment_count": 340,
      "rating": 4.7
    }
  ],
  "meta": {
    "page": 1,
    "limit": 20,
    "total": 156,
    "total_pages": 8
  },
  "links": {
    "self": "/api/v1/courses?page=1",
    "next": "/api/v1/courses?page=2",
    "last": "/api/v1/courses?page=8"
  }
}

Rate Limiting:

Free Tier:
- 1,000 requests/hour
- 10,000 requests/day
- No AI endpoints

Partner Tier ($99/month):
- 10,000 requests/hour
- 100,000 requests/day
- AI endpoints (limited)

Enterprise Tier (custom):
- Custom limits
- Dedicated support
- SLA guarantees

Documentation:

Tools:
- OpenAPI/Swagger specification
- Interactive API explorer
- Code examples (JavaScript, Python, PHP)
- Postman collection
- SDKs (JavaScript, Python)

Docs Site: developers.yebolearn.app

Sprint 28 Scope (8 points):

- [ ] API authentication (OAuth2)
- [ ] Core endpoints (courses, progress)
- [ ] Rate limiting middleware
- [ ] API documentation (Swagger)
- [ ] Beta partner onboarding

Not in scope:
- AI endpoints (Sprint 29)
- SDKs (Sprint 30)
- Enterprise features (Q2)

Integration Health

Monitoring

Integration Uptime (Last 30 Days):
- M-Pesa: N/A (not launched)
- WhatsApp: N/A (not launched)
- Gemini AI: 99.8%
- SendGrid: 99.9%
- Twilio: 99.5%
- Cloud Storage: 99.99%

Error Rates:
- Gemini AI: 1.2% (mostly rate limits)
- SendGrid: 0.3% (invalid emails)
- Twilio: 0.5% (invalid numbers)

Cost Tracking

November 2025 Integration Costs:
- Gemini AI: $200
- SendGrid: $45
- Twilio: $30
- Cloud Storage: $9
- Other: $16
Total: $300/month

Per Student Cost: $0.13/month
Revenue per Student: $5/month
Margin: 97.4% 💰

YeboLearn - Empowering African Education