BSS Payment QR Code & Event Flow Specification¶
Purpose: Define the payment QR code structure and event persistence flow between ABS and Odoo.
Key Principle: ABS is the source of truth for service and payment event history. Odoo consumes events to enrich CRM customer experience.
Payment QR Code Structure¶
Purpose¶
When quota is exhausted during battery swap, the attendant app displays a payment QR code that contains both Service Event and Payment Event data. This enables Odoo to: 1. Process payment (retail checkout function) 2. Update CRM records with service history 3. Provide transparency to customer
QR Code JSON Payload¶
{
"qr_type": "abs_payment_request",
"version": "1.0",
"service_event": {
"event_id": "SE-12345",
"event_type": "BATTERY_SWAP",
"timestamp": "2025-01-15T10:25:00Z",
"plan_id": "bss-plan-weekly-freedom-nairobi-v2-plan1",
"customer_id": "CUST-001",
"attendant_id": "ATT-001",
"station_id": "STATION_XYZ",
"batteries": {
"returned": {
"id": "BAT-12345",
"kwh": 4.8
},
"issued": {
"id": "BAT-67890",
"kwh": 30.4
},
"net_kwh_delivered": 25.6
},
"quota_consumption": {
"swap_count": 1,
"electricity_kwh": 25.6
}
},
"payment_event": {
"event_id": "PE-78910",
"event_type": "TOPUP_PAYMENT",
"timestamp": "2025-01-15T10:25:00Z",
"amount": 15.00,
"currency": "USD",
"merchant_station": "STATION_XYZ",
"service_description": "Battery Swap + Electricity Top-up",
"quota_deficit_kwh": 15.6,
"linked_service_event_id": "SE-12345"
},
"abs_metadata": {
"abs_version": "2.0.0",
"correlation_id": "TXN-12345",
"callback_url": "mqtt://abs-platform/payment/confirm/TXN-12345"
}
}
Field Descriptions¶
Service Event¶
| Field | Type | Description |
|---|---|---|
event_id |
string | Unique service event ID (ABS-generated) |
event_type |
string | Type of service (BATTERY_SWAP, FIRST_ISSUANCE, etc.) |
timestamp |
ISO 8601 | Event creation timestamp |
plan_id |
string | ServicePlan ID |
customer_id |
string | Customer ID |
attendant_id |
string | Attendant who performed service |
station_id |
string | Station/location ID |
batteries.returned |
object | Incoming battery details (null for first visit) |
batteries.issued |
object | Outgoing battery details |
batteries.net_kwh_delivered |
number | Net electricity delivered (outgoing - incoming) |
quota_consumption.swap_count |
number | Number of swaps consumed (typically 1) |
quota_consumption.electricity_kwh |
number | Electricity quota consumed |
Payment Event¶
| Field | Type | Description |
|---|---|---|
event_id |
string | Unique payment event ID (ABS-generated) |
event_type |
string | Type of payment (TOPUP_PAYMENT, RENEWAL_PAYMENT, etc.) |
timestamp |
ISO 8601 | Event creation timestamp |
amount |
number | Payment amount |
currency |
string | Currency code (USD, KES, etc.) |
merchant_station |
string | Station/merchant ID |
service_description |
string | Human-readable description |
quota_deficit_kwh |
number | Amount of quota deficit being paid for |
linked_service_event_id |
string | Reference to associated service event |
ABS Metadata¶
| Field | Type | Description |
|---|---|---|
abs_version |
string | ABS platform version |
correlation_id |
string | Transaction correlation ID for tracking |
callback_url |
string | MQTT topic for payment confirmation callback |
Payment Workflow¶
Step-by-Step Flow¶
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Attendant │ │ ABS Agent │ │ Odoo │ │ Rider │
│ App │ │ (BSS v2) │ │ ERP │ │ App │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │ │
│ 1. Scan outgoing │ │ │
│ battery │ │ │
├───────────────────>│ │ │
│ │ │ │
│ │ 2. Calculate quota │ │
│ │ (remaining < net)│ │
│ │ │ │
│ 3. QUOTA_EXHAUSTED │ │ │
│ + QR code data │ │ │
│<───────────────────┤ │ │
│ │ │ │
│ 4. Display QR code │ │ │
│ on screen │ │ │
│ │ │ │
│ │ │ 5. Scan QR │
│ │ │<────────────────────┤
│ │ │ │
│ │ │ 6. Payment page │
│ │ │ (pre-filled) │
│ │ │────────────────────>│
│ │ │ │
│ │ │ 7. Complete payment│
│ │ │<────────────────────┤
│ │ │ │
│ │ │ 8. Update CRM │
│ │ │ (from events) │
│ │ │ │
│ │ 9. Payment confirm │ │
│ │ (MQTT callback) │ │
│ │<────────────────────┤ │
│ │ │ │
│ 10. Payment received │ │
│ notification │ │ │
│<───────────────────┤ │ │
│ │ │ │
│ 11. Hand battery │ │ │
│ to rider │ │ │
│───────────────────────────────────────────────────────────────>│
│ │ │ │
│ 12. Service Complete │ │
├───────────────────>│ │ │
│ │ │ │
│ │ 13. Persist events │ │
│ │ to ABS DB │ │
│ │ │ │
│ │ 14. Update quotas │ │
│ │ & FSM states │ │
│ │ │ │
│ 15. Receipt │ │ │
│<───────────────────┤ │ │
│ │ │ │
Detailed Steps¶
| Step | Actor | Action | Data Flow |
|---|---|---|---|
| 1 | Attendant | Scans outgoing battery barcode | → ABS Agent |
| 2 | ABS Agent | Calculates quota: remaining_quota < net_kwh |
Internal |
| 3 | ABS Agent | Generates Service Event + Payment Event, returns QR data | → Attendant App |
| 4 | Attendant App | Displays QR code on screen | Visual |
| 5 | Rider | Scans QR code with rider app | → Odoo |
| 6 | Odoo | Opens payment page with pre-filled data from events | → Rider |
| 7 | Rider | Completes payment (mobile money, card, etc.) | → Odoo |
| 8 | Odoo | Updates CRM with service event history | Internal (Odoo) |
| 9 | Odoo | Sends payment confirmation via MQTT | → ABS Agent |
| 10 | ABS Agent | Notifies attendant app of payment receipt | → Attendant App |
| 11 | Attendant | Hands charged battery to rider | Physical |
| 12 | Attendant | Clicks "Service Complete" | → ABS Agent |
| 13 | ABS Agent | Persists Service Event + Payment Event to ABS database | → Database |
| 14 | ABS Agent | Updates quota consumption, FSM states | → ServicePlan |
| 15 | ABS Agent | Generates receipt with event history | → Attendant App |
Event Persistence¶
ABS Database Schema (Conceptual)¶
Service Events Table¶
CREATE TABLE service_events (
event_id VARCHAR PRIMARY KEY,
event_type VARCHAR NOT NULL,
timestamp TIMESTAMP NOT NULL,
plan_id VARCHAR NOT NULL REFERENCES service_plans(id),
customer_id VARCHAR NOT NULL,
attendant_id VARCHAR,
station_id VARCHAR,
battery_returned_id VARCHAR,
battery_returned_kwh DECIMAL(10,1),
battery_issued_id VARCHAR,
battery_issued_kwh DECIMAL(10,1),
net_kwh_delivered DECIMAL(10,1),
swap_count_consumed INT,
electricity_kwh_consumed DECIMAL(10,1),
created_at TIMESTAMP DEFAULT NOW()
);
Payment Events Table¶
CREATE TABLE payment_events (
event_id VARCHAR PRIMARY KEY,
event_type VARCHAR NOT NULL,
timestamp TIMESTAMP NOT NULL,
plan_id VARCHAR NOT NULL REFERENCES service_plans(id),
customer_id VARCHAR NOT NULL,
amount DECIMAL(10,2),
currency VARCHAR(3),
merchant_station VARCHAR,
service_description TEXT,
quota_deficit_kwh DECIMAL(10,1),
linked_service_event_id VARCHAR REFERENCES service_events(event_id),
odoo_receipt_id VARCHAR,
payment_method VARCHAR,
created_at TIMESTAMP DEFAULT NOW()
);
Event Lifecycle¶
┌────────────────────────────────────────────────────────────┐
│ Event Lifecycle │
└────────────────────────────────────────────────────────────┘
1. EVENT CREATION (when quota exhausted)
├─ ABS Agent creates Service Event
├─ ABS Agent creates Payment Event
└─ Events encoded in QR code
2. EVENT IN-FLIGHT (during payment)
├─ Events travel via QR code to Odoo
├─ Odoo uses events to populate payment page
└─ Odoo updates CRM customer history
3. EVENT CONFIRMATION (payment received)
├─ Odoo sends payment confirmation to ABS
├─ ABS Agent updates Payment Event with Odoo receipt ID
└─ Attendant app notified
4. EVENT PERSISTENCE (service complete)
├─ ABS Agent persists Service Event to database
├─ ABS Agent persists Payment Event to database
├─ ServicePlan quotas updated
└─ FSM states transitioned
5. EVENT HISTORY (transparency)
├─ ABS provides event history API
├─ Odoo can query event history for CRM
└─ Customer can view event history via app
ABS ↔ Odoo Integration Contract¶
Data Ownership¶
| Data Type | Owner | Consumer | Purpose |
|---|---|---|---|
| Service Events | ABS | Odoo | Service history, customer transparency |
| Payment Events | ABS | Odoo | Payment history, reconciliation |
| Payment Processing | Odoo | ABS | Payment confirmation, receipt ID |
| Customer CRM | Odoo | ABS | Customer details, subscription status |
| ServicePlan State | ABS | Odoo | Quota status, service availability |
API Contracts¶
1. ABS → Odoo: Payment Request (via QR Code)¶
Method: QR code scan
Data: Service Event + Payment Event (JSON)
Purpose: Initiate payment, provide context
2. Odoo → ABS: Payment Confirmation (MQTT)¶
Topic: mqtt://abs-platform/payment/confirm/{correlation_id}
Payload:
{
"correlation_id": "TXN-12345",
"payment_event_id": "PE-78910",
"odoo_receipt_id": "PAY-78910",
"payment_status": "SUCCESS",
"payment_method": "MOBILE_MONEY",
"payment_timestamp": "2025-01-15T10:24:30Z"
}
3. ABS → Odoo: Event History Query (GraphQL/REST)¶
Endpoint: GET /api/v1/service-events?customer_id={id}&limit=10
Response:
{
"service_events": [ /* array of service events */ ],
"payment_events": [ /* array of payment events */ ],
"total_count": 25,
"page": 1
}
Error Handling¶
Payment Timeout¶
Scenario: Rider scans QR but doesn't complete payment within 5 minutes.
Flow: 1. Attendant app displays "Payment Timeout" 2. Attendant can retry (generate new QR) or cancel service 3. If retry: New correlation_id generated, new QR displayed 4. Original events are not persisted until payment confirmed
Payment Failure¶
Scenario: Payment declined by payment processor.
Flow: 1. Odoo sends payment failure notification to ABS 2. ABS Agent notifies attendant app 3. Attendant app displays "Payment Declined - Retry?" 4. If retry: Same QR can be re-scanned, or new payment method attempted 5. Events not persisted until successful payment
Duplicate Payment¶
Scenario: Rider accidentally pays twice for same service.
Flow:
1. ABS checks correlation_id + payment_event_id uniqueness
2. If duplicate detected: Ignore second payment, flag for refund
3. Odoo CRM flags duplicate payment for manual resolution
4. Only one Payment Event persisted per Service Event
Receipt Generation¶
Receipt Content¶
The receipt combines Service Event + Payment Event data:
════════════════════════════════════════════
BATTERY SWAP SERVICE RECEIPT
════════════════════════════════════════════
Transaction ID: TXN-12345
Date/Time: 2025-01-15 10:30:00
Customer: John Doe (CUST-001)
Plan: Weekly Freedom Nairobi - Basic
────────────────────────────────────────────
SERVICE DETAILS
────────────────────────────────────────────
Battery Returned: BAT-12345 (4.8 kWh)
Battery Issued: BAT-67890 (30.4 kWh)
Net Electricity: 25.6 kWh
────────────────────────────────────────────
QUOTA CONSUMPTION
────────────────────────────────────────────
Swap Count: 1 swap
Electricity: 25.6 kWh
────────────────────────────────────────────
QUOTA REMAINING
────────────────────────────────────────────
Swap Count: 3 of 10 swaps
Electricity: 29.9 of 400 kWh
────────────────────────────────────────────
PAYMENT
────────────────────────────────────────────
Amount Paid: $15.00 USD
Method: Mobile Money
Receipt ID: PAY-78910
Timestamp: 2025-01-15 10:24:30
────────────────────────────────────────────
Service provided by: Station XYZ
Attendant: ATT-001
Thank you for using our service!
════════════════════════════════════════════
Implementation Checklist¶
Phase 1: QR Code Generation¶
- [ ] Implement Service Event creation logic
- [ ] Implement Payment Event creation logic
- [ ] Create QR code encoding function (JSON → QR)
- [ ] Add QR code display to attendant app UI
- [ ] Test QR code size (ensure scannable)
Phase 2: Odoo Integration¶
- [ ] Configure Odoo QR code scanner
- [ ] Implement JSON parsing in Odoo
- [ ] Create payment page pre-fill logic
- [ ] Implement CRM update from events
- [ ] Configure MQTT payment confirmation callback
Phase 3: Event Persistence¶
- [ ] Create
service_eventsdatabase table - [ ] Create
payment_eventsdatabase table - [ ] Implement event persistence functions
- [ ] Add foreign key constraints
- [ ] Create event history API endpoints
Phase 4: Testing¶
- [ ] Unit tests: Event creation, QR encoding
- [ ] Integration tests: QR scan → Payment → Confirmation
- [ ] Error scenarios: Timeout, failure, duplicate
- [ ] Load testing: Multiple simultaneous payments
- [ ] E2E testing: Complete workflow validation
Conclusion¶
This design ensures: 1. ABS as source of truth - All events persisted in ABS database 2. Odoo as payment processor - Handles retail checkout, CRM updates 3. Transparency - Event history available to all stakeholders 4. Auditability - Complete event trail from service to payment 5. Simplicity - QR code carries all necessary context
Next Steps: Proceed with implementation according to checklist above.