# Safaricom Status Query Integration

## Overview

The payment status endpoint now queries **Safaricom directly** for real-time payment status instead of relying on the local database. This ensures you always get the most accurate payment status.

---

## How It Works

### Flow Diagram

```
1. Client calls: POST /api/v1/payments/status
   ├─ Sends: client_id, checkout_id
   │
2. System looks up transaction in local DB
   ├─ Gets: ShortCode, PassKey, credentials
   │
3. System queries Safaricom
   ├─ Endpoint: /mpesa/stkpushquery/v1/query
   ├─ Sends: BusinessShortCode, Password, Timestamp, CheckoutRequestID
   │
4. Safaricom responds with ResultCode
   ├─ Code "0" = completed (success)
   ├─ Code "1032" = cancelled (user cancelled)
   ├─ Other codes = failed
   │
5. System updates local transaction record
   │
6. Returns response to client (without timestamps)
```

---

## Request Format

### Endpoint
```
POST /api/v1/payments/status
```

### Request Body
```json
{
  "client_id": "1fa69aeb-568e-4a14-92ae-7b50fa75e0fe",
  "checkout_id": "6RRGlABSLndXeT_xh7grvA=="
}
```

### Response (from Safaricom data)
```json
{
  "transaction_id": "e60288d4-e97c-45dd-a64a-cab33e20b693",
  "checkout_id": "6RRGlABSLndXeT_xh7grvA==",
  "status": "completed",
  "amount": 500,
  "reference": "STARTER-PL-20260128172108-8BoAUA",
  "phone_number": "254740782174"
}
```

---

## Result Code Mapping

| Safaricom Code | Meaning | Mapped Status |
|---|---|---|
| `0` | Success - Payment completed | `completed` |
| `1032` | Request cancelled by user | `cancelled` |
| Any other | Various errors (insufficient funds, invalid credentials, etc.) | `failed` |

---

## Implementation Details

### Service Method: `querySafaricomPaymentStatus()`

```go
func (s *PaymentService) querySafaricomPaymentStatus(
    ctx context.Context, 
    credentials *models.PaymentCredentials, 
    checkoutID string
) (*SafaricomStatusResponse, error)
```

**What it does:**
1. Gets OAuth access token from Safaricom
2. Creates request with:
   - BusinessShortCode (from credentials)
   - Password (base64 encoded: ShortCode + PassKey + Timestamp)
   - Timestamp (current time in YYYYMMDDHHmmss format)
   - CheckoutRequestID (the original checkout ID)
3. Sends POST request to Safaricom's query endpoint
4. Parses and returns the response

**Error Handling:**
- Returns error if token retrieval fails
- Returns error if HTTP request fails
- Returns error if response cannot be decoded
- Includes Safaricom's error description in error message

---

## Key Features

### ✅ Real-time Data
- Always queries Safaricom for current status
- No reliance on callback timing
- Accurate even if callback was delayed or failed

### ✅ Automatic Local Update
- Safaricom result is stored in local transaction record
- Ensures system stays in sync
- Prevents stale data in database

### ✅ Secure
- Uses OAuth Bearer token authentication
- Credentials never exposed in logs
- HTTPS communication with Safaricom

### ✅ Efficient
- Token caching to reduce API calls
- Only one database query for transaction lookup
- Single API call to Safaricom (no redundant queries)

---

## Example Scenarios

### Scenario 1: Successful Payment
```json
{
  "client_id": "1fa69aeb-568e-4a14-92ae-7b50fa75e0fe",
  "checkout_id": "6RRGlABSLndXeT_xh7grvA=="
}
```

**Safaricom returns:** ResultCode = "0"
**Response status:** `"completed"`

---

### Scenario 2: User Cancelled Payment
```json
{
  "client_id": "1fa69aeb-568e-4a14-92ae-7b50fa75e0fe",
  "checkout_id": "6RRGlABSLndXeT_xh7grvA=="
}
```

**Safaricom returns:** ResultCode = "1032"
**Response status:** `"cancelled"`

---

### Scenario 3: Transaction Not Found
```json
{
  "client_id": "1fa69aeb-568e-4a14-92ae-7b50fa75e0fe",
  "checkout_id": "invalid-checkout-id"
}
```

**Response:** 404 Not Found
```json
{
  "error": "transaction not found in local database"
}
```

---

## Error Responses

### Local Database Error
```
404 Not Found
{
  "error": "transaction not found in local database"
}
```

### Safaricom Query Error
```
500 Internal Server Error
{
  "error": "failed to query Safaricom payment status: [details]"
}
```

### Invalid Client
```
403 Forbidden
{
  "error": "Invalid or inactive client"
}
```

---

## Performance Considerations

### Token Caching
- Access tokens are cached in Redis
- Token expires and is automatically refreshed
- Reduces redundant token requests

### Database Queries
- Single query to look up transaction
- No heavy queries or joins
- Fast response times

### API Calls
- One call to Safaricom per status check
- No retry logic (fails fast)
- 30-second timeout per request

---

## Testing

### Test Endpoint
```bash
curl -X POST http://localhost:8080/api/v1/payments/status \
  -H "Content-Type: application/json" \
  -d '{
    "client_id": "1fa69aeb-568e-4a14-92ae-7b50fa75e0fe",
    "checkout_id": "6RRGlABSLndXeT_xh7grvA=="
  }'
```

### Expected Response (if payment completed)
```json
{
  "transaction_id": "e60288d4-e97c-45dd-a64a-cab33e20b693",
  "checkout_id": "6RRGlABSLndXeT_xh7grvA==",
  "status": "completed",
  "amount": 500,
  "reference": "STARTER-PL-20260128172108-8BoAUA",
  "phone_number": "254740782174"
}
```

---

## Configuration Required

### Safaricom API Credentials
Stored in `payment_credentials` table:
- `BusinessShortCode` - Your M-Pesa shortcode
- `ConsumerKey` - OAuth consumer key
- `ConsumerSecret` - OAuth consumer secret
- `PassKey` - M-Pesa passkey for encryption
- `BaseURL` - Safaricom API base URL

### Ensure These Are Set
```bash
# Example values (use actual credentials)
ShortCode: 174379
ConsumerKey: xB5vGjK9pQwR2sTuVwXyZ1a2b3c4d5e6f
ConsumerSecret: mN8oP9q0rStUvWxYzA1b2c3d4e5f6g7h8
PassKey: bfb279f9aa9bdbcf158e97dd1a7c6e676e06d3d616be906227b8f641b681425
BaseURL: https://sandbox.safaricom.co.ke (sandbox) or production URL
```

---

## Files Modified

1. **[internal/services/payment_service.go](internal/services/payment_service.go)**
   - Removed `CreatedAt`, `UpdatedAt` from `PaymentStatusResponse`
   - Updated `CheckPaymentStatus()` to query Safaricom
   - Added `querySafaricomPaymentStatus()` method
   - Added `SafaricomStatusResponse` struct
   - Added `mapSafaricomResultCode()` method

2. **Documentation**
   - [docs/PUBLIC_PAYMENT_API.md](docs/PUBLIC_PAYMENT_API.md)
   - [PAYMENT_API_QUICK_REFERENCE.md](PAYMENT_API_QUICK_REFERENCE.md)
   - [PAYMENT_API_TEST_EXAMPLE.md](PAYMENT_API_TEST_EXAMPLE.md)

---

## Summary

✅ Payment status is now queried directly from Safaricom
✅ Real-time, accurate payment status
✅ No timestamp fields in response (cleaner API)
✅ Local database automatically synced with Safaricom
✅ Efficient token caching and request handling
✅ Comprehensive error handling
