# FinFlo Direct Debit â€“ Developer Implementation Guide

This guide explains how to integrate FinFlo Direct Debit into FinFlo in-house apps (e.g. Didi Riders). Users enter their FinFlo phone and PIN in your app; FinFlo debits their balance and credits your platform app wallet.

---

## Overview

| Flow | Description |
|------|-------------|
| **API flow** | Your backend sends `app_slug`, `phone`, `pin`, `amount`; FinFlo processes the debit and returns a result. |
| **Hosted checkout** | User is redirected to FinFloâ€™s hosted payment page, enters phone and PIN, then is redirected back to your app. |

**Credit destination (hybrid):** No-wallet apps (e.g. Bet Tips) credit FinFlo central wallet. Wallet apps (e.g. Didi Riders) credit the app wallet. Both flows appâ€™s wallet on success.

---

## Prerequisites

1. Your platform app must be registered in FinFlo (Admin â†’ Platform Apps or via migration).
2. You must have the **in-house API key** (single key for all in-house apps; Admin â†’ API Keys).
3. Base URL: `https://finflo.net/public/v1` (or your environmentâ€™s base URL).

---

## Authentication

One shared API key for all in-house apps. Include this header on every API request:

```
X-API-Key: your_inhouse_api_key_here
Content-Type: application/json
Accept: application/json
```

**Security:** Do not expose the API key in public-facing client apps. Use it only on your backend.

---

## Option 1: Direct API

### Endpoint

```
POST /v1/api/payments/direct-debit
```

### Request body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `app_slug` | string | Yes | Platform app slug (e.g. `didi-riders`, `bet-tips`) |
| `phone` | string | Yes | FinFlo phone (e.g. `256700000000`) |
| `pin` | string | Yes | Userâ€™s 4â€“6 digit PIN |
| `amount` | number | Yes | Amount in UGX (e.g. `10000`) |
| `reference` | string | No | Your internal reference |
| `description` | string | No | Payment description |

### Example request

```bash
curl -X POST "https://finflo.net/public/v1/api/payments/direct-debit" \
  -H "X-API-Key: your_inhouse_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "app_slug": "didi-riders",
    "phone": "256700000000",
    "pin": "1234",
    "amount": 10000,
    "reference": "ORDER-12345",
    "description": "Didi Riders wallet top-up"
  }'
```

### Success response (200)

```json
{
  "status": "success",
  "message": "Payment successful",
  "data": {
    "tx_ref": "TX-260215-A4B9C2",
    "status": "COMPLETED",
    "amount": 10000,
    "balance_after": 50000,
    "transaction_id": 12345
  },
  "timestamp": "2026-02-15T00:00:00+00:00"
}
```

### Error responses

| Status | Error | Description |
|--------|-------|-------------|
| 400 | `User not found with this phone number` | Phone not registered on FinFlo |
| 400 | `Insufficient balance. Available: X UGX` | User balance too low |
| 400 | `Account is frozen...` | Account frozen |
| 401 | `Invalid PIN` | Wrong PIN (rate-limited after 5 failures) |
| 401 | `Invalid or missing API key` | Auth header missing or invalid |

Example error body:

```json
{
  "status": "error",
  "message": "Insufficient balance. Available: 5000 UGX",
  "timestamp": "2026-02-15T00:00:00+00:00"
}
```

---

## Option 2: Hosted checkout

Use this when you want FinFlo to host the payment form so you donâ€™t collect PIN in your app.

### Step 1: Create session

```
POST /v1/api/payments/direct-debit/create-session
```

**Request body:**

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `app_slug` | string | Yes | Platform app slug (e.g. `didi-riders`, `bet-tips`) |
| `amount` | number | Yes | Amount in UGX |
| `reference` | string | No | Your reference |
| `callback_url` | string | Yes | URL to redirect to on success |
| `cancel_url` | string | No | URL to redirect to on cancel |

**Example:**

```bash
curl -X POST "https://finflo.net/public/v1/api/payments/direct-debit/create-session" \
  -H "X-API-Key: your_inhouse_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "app_slug": "didi-riders",
    "amount": 10000,
    "reference": "ORDER-12345",
    "callback_url": "https://yourapp.com/payment/success",
    "cancel_url": "https://yourapp.com/payment/cancel"
  }'
```

**Response:**

```json
{
  "status": "success",
  "data": {
    "session_id": "abc123...",
    "pay_url": "https://finflo.net/pay?s=abc123...",
    "expires_in": 900
  }
}
```

Sessions expire after 15 minutes (`expires_in` is in seconds).

### Step 2: Redirect user

Redirect the user to `pay_url`:

```
https://finflo.net/pay?s=<session_id>
```

The user sees the amount, app name, and enters their FinFlo phone and PIN.

### Step 3: Handle callback

On success, the user is redirected to your `callback_url` with query parameters:

```
https://yourapp.com/payment/success?tx_ref=TX-260215-A4B9C2&status=COMPLETED&amount=10000
```

Your backend should:

1. Verify `status === "COMPLETED"`.
2. Use `tx_ref` to confirm with FinFlo if needed.
3. Credit your userâ€™s wallet/account.

On cancel, the user is sent to `cancel_url` (if provided).

---

## Webhooks (optional)

If your platform app has a webhook URL configured, FinFlo will POST the following on a successful debit:

```json
{
  "tx_ref": "TX-260215-A4B9C2",
  "status": "COMPLETED",
  "amount": 10000,
  "user_phone": "256700000000",
  "reference": "ORDER-12345"
}
```

Ensure your endpoint:

- Returns 2xx for success.
- Is publicly reachable.
- Validates the request as coming from FinFlo (e.g. via shared secret if available).

---

## Role-based deductions

FinFlo debits based on user role:

| Role | Balance source |
|------|----------------|
| USER | Wallet balance |
| AGENT | Working capital (`float_balance`) |
| MERCHANT | Merchant wallet balance |

No configuration is required on your side; the correct source is used automatically.

---

## Integration checklist

- [ ] Obtain in-house API key (Admin â†’ API Keys)
- [ ] Implement Direct API and/or Hosted checkout in your backend
- [ ] Add error handling for invalid PIN, insufficient balance, and network errors
- [ ] Store `tx_ref` for reconciliation
- [ ] Configure webhook URL if you want real-time notifications
- [ ] Test with a sandbox/test account before going live

---

## Quick reference

| Item | Value |
|------|-------|
| Base URL | `https://finflo.net/public/v1` |
| Direct debit | `POST /api/payments/direct-debit` |
| Create session | `POST /api/payments/direct-debit/create-session` |
| Hosted page | `https://finflo.net/pay?s={session_id}` |
| Currency | UGX |
| Session TTL | 15 minutes |

---

## Support

For API keys, configuration, or integration issues, contact the FinFlo development team.
