# Paystack Payment System Architecture Explanation

## Overview
The system maintains **two completely separate Paystack payment flows**:
1. **Store-Specific Paystack** - For customer orders at individual stores
2. **Base/Global Paystack** - For store owner top-ups (wallet recharge)

---

## 1. Store-Specific Paystack System (Customer Orders)

### Purpose
Allows each store owner to integrate their own Paystack account so customers pay **directly to the store owner's Paystack account** instead of using mobile money.

### How It Works

#### A. Configuration Storage
- **Table**: `store_paystack_credentials`
- **Fields**:
  - `store_id` (unique per store)
  - `public_key` (store owner's Paystack public key)
  - `secret_key` (store owner's Paystack secret key)
  - `is_active` (enabled/disabled flag)

#### B. Payment Initiation (`process_store_order.php`)

**Step 1: Check if Store Has Paystack Enabled**
```php
// Line 230-235: Check store-specific credentials
$paystackStmt = $conn->prepare("
    SELECT public_key, secret_key, is_active 
    FROM store_paystack_credentials 
    WHERE store_id = ? AND is_active = 1
");
```

**Step 2: Use Store-Specific Config**
```php
// Line 276: Get store-specific Paystack config
$paystackConfig = getPaystackConfig(true, $storeId);
// This function checks store_paystack_credentials table FIRST
```

**Step 3: Initialize Payment with Store's Keys**
```php
// Line 308: Uses store owner's secret key
'Authorization: Bearer ' . $paystackConfig['secret_key']
```

**Step 4: Store Paystack Reference**
```php
// Line 246: Generate unique reference
$paystackReference = 'STORE_' . $orderReference . '_' . time();

// Line 267: Save to orders table
UPDATE orders SET paystack_reference = ? WHERE id = ?
```

#### C. Payment Verification (`store_paystack_callback.php`)

**Step 1: Find Order by Paystack Reference**
```php
// Line 26-34: Look up order using paystack_reference
SELECT o.*, s.store_slug
FROM orders o
INNER JOIN stores s ON o.store_id = s.id
WHERE o.paystack_reference = ?
```

**Step 2: Verify Payment Using Store's Credentials**
```php
// Line 65: Pass store_id to verification function
$verify = paystackVerifyTransaction($reference, $order['store_id']);
// This ensures we use the CORRECT store's secret key
```

**Step 3: Process Order Automatically**
- Deduct admin cost from store owner's balance
- Send order to external API (if enabled)
- Update order status to 'processing'

---

## 2. Base/Global Paystack System (Store Owner Top-ups)

### Purpose
Allows store owners to **top up their wallet balance** using the admin's Paystack account.

### How It Works

#### A. Configuration Storage
- **Table**: `payment_config`
- **Fields**:
  - `config_key` = 'paystack_public_key'
  - `config_key` = 'paystack_secret_key'
  - `config_key` = 'paystack_base_url'
- **Alternative**: Environment variables (`PAYSTACK_PUBLIC_KEY`, `PAYSTACK_SECRET_KEY`)

#### B. Payment Initiation (`api/store_owner_topup.php`)

**Step 1: Get Base Paystack Config**
```php
// Line 261: Get base config (NO store_id parameter)
$paystackConfig = getPaystackConfig(true);
// When storeId is NULL, it loads from payment_config table
```

**Step 2: Create Payment Intent**
```php
// Line 230-234: Store payment intent in orders_payments table
INSERT INTO orders_payments 
(user_id, total_amount, payment_method, payment_status, payment_reference, 
 payment_type, payment_date, created_at) 
VALUES (?, ?, 'paystack', 'pending', ?, 'topup', NOW(), NOW())
```

**Step 3: Initialize with Base Keys**
```php
// Uses admin's Paystack secret key (from payment_config)
'Authorization: Bearer ' . $paystackConfig['secret_key']
```

#### C. Payment Verification (`paystack_callback.php`)

**Step 1: Find Payment Intent**
```php
// Line 103-118: Look up by payment_reference in orders_payments
SELECT user_id, total_amount, payment_status, payment_type
FROM orders_payments
WHERE payment_reference = ? AND payment_type = 'topup'
```

**Step 2: Verify Payment Using Base Config**
```php
// Line 31: Uses base Paystack config (NO store_id)
$verify = paystackVerifyTransaction($reference);
// When storeId is NULL, uses payment_config keys
```

**Step 3: Credit Store Owner's Balance**
- Add amount to store owner's wallet balance
- Update payment status to 'completed'
- Redirect to store dashboard

---

## 3. How the System Keeps Them Separate

### Key Differentiation Mechanisms

#### 1. **Database Tables**
- **Store Payments**: Use `orders` table with `paystack_reference` column
- **Top-ups**: Use `orders_payments` table with `payment_type = 'topup'`

#### 2. **Reference Format**
- **Store Payments**: `STORE_{ORDER_REF}_{TIMESTAMP}` (e.g., `STORE_ABCD_1234567890`)
- **Top-ups**: Generated by `generatePaymentReference()` function (different format)

#### 3. **Config Function Logic** (`config_paystack.php`)

```php
function getPaystackConfig($force_reload = false, $storeId = null) {
    // If storeId is provided:
    if ($storeId) {
        // 1. Check store_paystack_credentials table FIRST
        // 2. If found and active, use store-specific keys
        // 3. If not found, fall back to base config
    }
    
    // If storeId is NULL:
    // Always use base config from payment_config table
}
```

#### 4. **Callback URLs**
- **Store Payments**: `store_paystack_callback.php`
- **Top-ups**: `paystack_callback.php`

#### 5. **Verification Function** (`paystackVerifyTransaction`)

```php
function paystackVerifyTransaction(string $reference, $storeId = null): array {
    // If storeId provided: Use store-specific secret key
    // If storeId is NULL: Use base secret key from payment_config
    
    $config = getPaystackConfig(true, $storeId);
    // Uses the appropriate secret key based on storeId parameter
}
```

---

## 4. Complete Payment Flow Comparison

### Store Order Payment Flow
```
Customer places order
    ↓
process_store_order.php checks store_paystack_credentials
    ↓
If store has Paystack enabled:
    - Get store-specific keys (getPaystackConfig(true, $storeId))
    - Initialize payment with store's public key
    - Save paystack_reference to orders table
    ↓
Customer redirected to Paystack (store owner's account)
    ↓
Customer pays → Money goes to STORE OWNER's Paystack account
    ↓
Paystack redirects to store_paystack_callback.php
    ↓
Callback verifies payment using store's secret key
    - paystackVerifyTransaction($reference, $storeId)
    - Uses store-specific secret key
    ↓
Order automatically processed
    - Deduct admin cost from store owner's balance
    - Send to external API
```

### Top-up Payment Flow
```
Store owner requests top-up
    ↓
api/store_owner_topup.php uses base Paystack config
    - getPaystackConfig(true) [NO storeId]
    - Gets keys from payment_config table
    ↓
Create payment intent in orders_payments table
    - payment_type = 'topup'
    ↓
Store owner redirected to Paystack (ADMIN's account)
    ↓
Store owner pays → Money goes to ADMIN's Paystack account
    ↓
Paystack redirects to paystack_callback.php
    ↓
Callback verifies payment using base secret key
    - paystackVerifyTransaction($reference) [NO storeId]
    - Uses base secret key from payment_config
    ↓
Store owner's wallet balance credited
```

---

## 5. Security & Isolation

### Why This Design is Secure

1. **Separate Database Tables**
   - Store orders: `orders` table
   - Top-ups: `orders_payments` table
   - No cross-contamination possible

2. **Separate Callback Handlers**
   - Store: `store_paystack_callback.php`
   - Top-up: `paystack_callback.php`
   - Different logic, different verification methods

3. **Config Function Isolation**
   - Store-specific: Always checks `store_paystack_credentials` first
   - Base: Always uses `payment_config` when `storeId` is NULL
   - Fallback logic ensures base config is never used for store payments

4. **Reference Format Distinction**
   - Store references: `STORE_` prefix
   - Top-up references: Different format
   - Easy to identify payment type

5. **Payment Type Tracking**
   - Top-ups: `payment_type = 'topup'` in `orders_payments`
   - Store orders: No `payment_type` field (different table)

---

## 6. Key Code Locations

### Store-Specific Paystack
- **Config**: `config_paystack.php` - `getPaystackConfig($force_reload, $storeId)`
- **Initiation**: `process_store_order.php` (lines 230-350)
- **Callback**: `store_paystack_callback.php`
- **Settings UI**: `pages/store_paystack_settings.php`
- **Database**: `store_paystack_credentials` table

### Base Paystack (Top-ups)
- **Config**: `config_paystack.php` - `getPaystackConfig($force_reload)` [no storeId]
- **Initiation**: `api/store_owner_topup.php`
- **Callback**: `paystack_callback.php`
- **Database**: `payment_config` table

---

## 7. Summary

**The system maintains complete separation through:**

1. ✅ **Different database tables** (orders vs orders_payments)
2. ✅ **Different callback handlers** (store_paystack_callback.php vs paystack_callback.php)
3. ✅ **Different reference formats** (STORE_ prefix vs topup format)
4. ✅ **Config function parameter** (storeId determines which keys to use)
5. ✅ **Payment type tracking** (topup type in orders_payments)
6. ✅ **Separate credential storage** (store_paystack_credentials vs payment_config)

**Result**: Store owners can have their own Paystack accounts for customer payments, while still using the admin's Paystack account for wallet top-ups. The two systems never interfere with each other.

