improving error message on batch item processing
This commit is contained in:
317
docs/PRODUCT_SPEC.md
Normal file
317
docs/PRODUCT_SPEC.md
Normal file
@@ -0,0 +1,317 @@
|
|||||||
|
# Velocity Pay — Product Specification
|
||||||
|
|
||||||
|
**Version:** 1.0.1
|
||||||
|
**Last Updated:** January 2026
|
||||||
|
**Product Owner:** Velocity Africa (Qantra)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Executive Summary
|
||||||
|
|
||||||
|
Velocity Pay is a multi-platform bill-payment application that enables individuals and businesses in Zimbabwe to pay utilities, purchase airtime, and send batch payments through a single, streamlined experience. The application supports EcoCash, Mastercard/Visa (MPGS), and wallet-based payment processors, and is available across Android, iOS, and the web.
|
||||||
|
|
||||||
|
Built with Flutter, Velocity Pay delivers a consistent, high-quality user experience on mobile devices and desktop browsers alike. It is designed from the ground up to support multi-workspace / multi-tenant scenarios, making it suitable for both individual consumers and organisations that manage multiple business entities.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Target Audience
|
||||||
|
|
||||||
|
| Segment | Description |
|
||||||
|
|---------|-------------|
|
||||||
|
| **Individual consumers** | Pay utility bills (ZESA electricity, water), purchase airtime (Econet, NetOne, Telecel), and top up mobile wallets. |
|
||||||
|
| **Small businesses** | Manage payments across a single workspace with history tracking and repeat transactions. |
|
||||||
|
| **Enterprises / NGOs** | Use batch payments via recipient **Groups** to disburse funds to many recipients at once (e.g. payroll, beneficiary disbursements). |
|
||||||
|
| **Developers / Integrators** | Organisations that need to embed payment functionality into their own systems can reference Velocity Pay's gateway integration patterns. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Supported Platforms
|
||||||
|
|
||||||
|
| Platform | Status | Notes |
|
||||||
|
|----------|--------|-------|
|
||||||
|
| **Android** | GA | Native APK / App Bundle distribution via Google Play. |
|
||||||
|
| **iOS** | GA | Native IPA distribution via the App Store. |
|
||||||
|
| **Web** | GA | Progressive Web App (PWA) served via Nginx. Works on all modern browsers (Chrome, Firefox, Safari, Edge). |
|
||||||
|
| **Linux / macOS / Windows** | Beta | Desktop builds available for internal/admin use. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Core Feature Set
|
||||||
|
|
||||||
|
### 4.1 Onboarding & Authentication
|
||||||
|
|
||||||
|
- **Phone number sign-in:** Users enter their mobile number, receive an OTP verification code, and authenticate.
|
||||||
|
- **Google Sign-In:** OAuth-based authentication for web users (desktop and mobile web).
|
||||||
|
- **Persisted sessions:** Tokens are stored securely on-device so users remain signed in across cold starts.
|
||||||
|
- **Splash animation:** A branded loading animation plays on cold start before the app routes to the appropriate screen.
|
||||||
|
|
||||||
|
### 4.2 Workspaces
|
||||||
|
|
||||||
|
- After authentication, users are presented with a workspace selector if they belong to multiple organisations.
|
||||||
|
- A workspace bundles a user's billing accounts, transaction history, and groups under one organisational context.
|
||||||
|
- Single-workspace users are auto-routed directly to the home screen.
|
||||||
|
- Guest (unauthenticated) users receive a transient workspace UUID so they can still make one-off payments.
|
||||||
|
|
||||||
|
### 4.3 Home Dashboard
|
||||||
|
|
||||||
|
- **Account balance widget:** Displays the current balance for the authenticated user's workspace. Includes a currency selector (USD, ZWG, etc.).
|
||||||
|
- **Bill provider directory:** A dynamic, API-driven grid of available billers (Econet, NetOne, ZESA, Telecel, etc.). Each card shows the provider's logo, name, and description.
|
||||||
|
- **Recent activity feed:** A chronological list of the user's most recent transactions with status chips (Success / Pending / Failed). Each transaction includes a **Repeat** action that pre-fills the payment flow with the same details.
|
||||||
|
- **Pull-to-refresh:** Manually reload balances, providers, and transactions.
|
||||||
|
- **Responsive layout:** On mobile the UI uses a full-bleed SliverAppBar and bottom NavigationBar; on tablets and desktop it uses a side NavigationRail.
|
||||||
|
|
||||||
|
### 4.4 Payment Flow
|
||||||
|
|
||||||
|
The payment flow is a multi-step wizard with the following stages:
|
||||||
|
|
||||||
|
| Step | Screen | Description |
|
||||||
|
|------|--------|-------------|
|
||||||
|
| 1 | **Home → Provider Select** | User taps a bill provider (e.g. Econet, ZESA). |
|
||||||
|
| 2 | **Recipients** | Choose a saved contact, enter a phone number manually, or pick from the device's native contacts. |
|
||||||
|
| 3 | **Product Selection** (conditional) | If the provider offers multiple products (e.g. airtime bundles, electricity tokens), the user picks the specific product and amount. |
|
||||||
|
| 4 | **Pay** | Enter payment amount (when not fixed by product), select the payment processor (EcoCash / MPGS / Wallet), enter the debit phone number, and optionally record the recipient's name and email for a receipt. |
|
||||||
|
| 5 | **Confirm** | Review all transaction details before submission. The user sees a line-item breakdown: amount, charge, gateway charge, tax, and total. |
|
||||||
|
| 6 | **Gateway** | The app opens the payment processor's checkout page (hosted WebView for Mastercard, or redirect for mobile wallets). The user completes payment authorisation. |
|
||||||
|
| 7 | **Polling** | The app polls the backend for the transaction result for up to 30 attempts (~90 seconds). Progress is indicated by a step counter and status text. |
|
||||||
|
| 8 | **Integration** | On success, the backend posts the payment to the billing provider (e.g. credits the electricity meter). A loading indicator shows this progress. |
|
||||||
|
| 9 | **Receipt** | A full transaction receipt is displayed with share capabilities (image export via `share_plus`). The receipt includes transaction ID, date, amount, charges, provider, and status. |
|
||||||
|
|
||||||
|
**Error handling at every step:** Network errors, failed payments, and integration failures are surfaced with human-readable messages and a **Retry** button. Users can cancel polling at any time.
|
||||||
|
|
||||||
|
### 4.5 Payment Processors
|
||||||
|
|
||||||
|
| Processor | Label | Description |
|
||||||
|
|-----------|-------|-------------|
|
||||||
|
| **EcoCash** | `ECOCASH` | Zimbabwe's mobile money wallet. Authorisation via USSD push or in-app prompt. |
|
||||||
|
| **Mastercard Payment Gateway Services** | `MPGS` | Card payments (Visa, Mastercard). Hosted checkout page in a WebView. |
|
||||||
|
| **Wallet** | `WALLET` | In-app wallet balance (available only to authenticated users with a funded workspace wallet). |
|
||||||
|
|
||||||
|
### 4.6 Transaction History
|
||||||
|
|
||||||
|
- Full, searchable history of all past transactions for the current workspace.
|
||||||
|
- Each transaction item displays: provider icon, provider name, amount, date (relative time), and a coloured status chip.
|
||||||
|
- **Repeat transaction:** One-tap repeat that pre-fills the payment flow with the same provider, product, amount, and payment processor.
|
||||||
|
- **Receipt view:** Tap any historical transaction to view its full receipt.
|
||||||
|
|
||||||
|
### 4.7 Groups & Batch Payments
|
||||||
|
|
||||||
|
Groups allow users to organise recipients and send batch payments efficiently.
|
||||||
|
|
||||||
|
- **Group creation:**
|
||||||
|
- **Manual form:** Enter a group name, description, and add recipients one at a time.
|
||||||
|
- **CSV file import:** Upload a CSV file containing recipient names, phone numbers, amounts, and email addresses. A sample CSV template is provided.
|
||||||
|
- **Group management:** View all groups, search by name, select multiple groups for bulk deletion.
|
||||||
|
- **Batch creation:** Within a group, create a payment batch that processes all recipients. Each batch is a discrete payment run with its own status.
|
||||||
|
- **Batch detail:** View the status of each item (recipient) in a batch. Individual batch items can be inspected for success/failure details.
|
||||||
|
|
||||||
|
### 4.8 Users & Permissions
|
||||||
|
|
||||||
|
- Authenticated workspace admins can view and manage the list of users associated with their workspace (accessible from the **More** menu).
|
||||||
|
- User listing is available at `/users` and is protected behind an auth guard.
|
||||||
|
|
||||||
|
### 4.9 Uptime & Service Status
|
||||||
|
|
||||||
|
- A real-time service status dashboard showing the operational state of every integrated bill provider and payment processor.
|
||||||
|
- Each service displays:
|
||||||
|
- Service name and icon (EcoCash, Econet, ZESA, etc.)
|
||||||
|
- Up/Down status indicator (green/red chip)
|
||||||
|
- Last-checked timestamp (relative time)
|
||||||
|
- Summary bar shows total counts of Up vs Down services.
|
||||||
|
- Data is fetched on-screen load and supports pull-to-refresh.
|
||||||
|
|
||||||
|
### 4.10 Profile & Account Management
|
||||||
|
|
||||||
|
- **Profile:** View account details (accessible from `/profile`).
|
||||||
|
- **Change Workspace:** Switch to a different workspace without logging out.
|
||||||
|
- **Logout:** Clears local session and returns to the landing screen.
|
||||||
|
- **Account Deletion:** GDPR/Play Store compliant account deletion request flow at `/delete-account`.
|
||||||
|
|
||||||
|
### 4.11 Contact & Support
|
||||||
|
|
||||||
|
- Dedicated contact screen accessible from the **More** menu.
|
||||||
|
- Footer links in the web navigation rail provide quick access to About, FAQs, Privacy Policy, and Terms of Service on the Velocity Africa website.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Architecture & Technology
|
||||||
|
|
||||||
|
| Layer | Technology |
|
||||||
|
|-------|-----------|
|
||||||
|
| **Framework** | Flutter (Dart 3.9+) |
|
||||||
|
| **State Management** | Provider + ChangeNotifier (with Freezed immutable models) |
|
||||||
|
| **Routing** | GoRouter (declarative, path-based, with auth guards) |
|
||||||
|
| **HTTP Client** | Dio |
|
||||||
|
| **Code Generation** | Freezed + json_serializable + build_runner |
|
||||||
|
| **Local Storage** | SharedPreferences (token, workspace ID, user preferences) |
|
||||||
|
| **Backend** | Velocity Pay API (`payapi.velocityafrica.net/api`) |
|
||||||
|
| **Auth** | OAuth 2.0 (Google) + Phone OTP |
|
||||||
|
| **Payment Gateway** | Mastercard MPGS (hosted checkout via WebView + JavaScript) |
|
||||||
|
| **Push Notifications** | Firebase Cloud Messaging |
|
||||||
|
| **Analytics** | Firebase |
|
||||||
|
| **Deployment** | Docker + Nginx (web), Google Play (Android), App Store (iOS) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Environment Configuration
|
||||||
|
|
||||||
|
Velocity Pay supports compile-time environment switching via `--dart-define` flags:
|
||||||
|
|
||||||
|
| Flag | Values | Description |
|
||||||
|
|------|--------|-------------|
|
||||||
|
| `APP_ENV` | `test`, `live` | Selects the runtime environment. Defaults to `test`. |
|
||||||
|
| `BASE_URL` | Any URL | Overrides the backend API base URL. |
|
||||||
|
| `CLIENTID` | String | Google OAuth web client ID. |
|
||||||
|
| `SIMULATE_PAYMENT_SUCCESS` | `true`, `false` | When `true`, the gateway flow returns a simulated success (development only). |
|
||||||
|
| `GATEWAY_SCRIPT_URL` | URL | Mastercard checkout JavaScript bundle URL. |
|
||||||
|
|
||||||
|
**Pre-release checklist:**
|
||||||
|
- `assets/.env.live` points to the live API.
|
||||||
|
- `SIMULATE_PAYMENT_SUCCESS` is set to `false`.
|
||||||
|
- `GATEWAY_SCRIPT_URL` uses the production Mastercard script.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Navigation Structure
|
||||||
|
|
||||||
|
### Authenticated Shell (with nav bar / nav rail)
|
||||||
|
|
||||||
|
```
|
||||||
|
/home → Home dashboard (providers + recent activity)
|
||||||
|
/groups → Group listing
|
||||||
|
/groups/create → Create group (manual form)
|
||||||
|
/groups/create-from-file → Create group (CSV upload)
|
||||||
|
/groups/:groupId → Group detail
|
||||||
|
/groups/:groupId/batches/create → Create batch for group
|
||||||
|
/groups/batches/:batchId → Batch detail
|
||||||
|
/history → Full transaction history
|
||||||
|
/more → More menu (workspace switch, users, uptime, contact, logout)
|
||||||
|
/users → User management
|
||||||
|
/uptime → Service uptime status
|
||||||
|
/profile → User profile
|
||||||
|
/contact → Contact support
|
||||||
|
/workspace → Workspace selector
|
||||||
|
```
|
||||||
|
|
||||||
|
### Payment Flow (no nav in shell — full-screen wizard)
|
||||||
|
|
||||||
|
```
|
||||||
|
/recipients → Recipient selection
|
||||||
|
/make-payment → Payment form (amount, processor)
|
||||||
|
/confirm → Transaction confirmation
|
||||||
|
/gateway → Payment gateway (native WebView)
|
||||||
|
/gateway-web → Payment gateway (web)
|
||||||
|
/gateway-redirect → Gateway redirect handler
|
||||||
|
/poll → Transaction status polling
|
||||||
|
/poll/:id → Poll for specific transaction
|
||||||
|
/integration → Post-payment billing integration
|
||||||
|
/receipt → Transaction receipt
|
||||||
|
/receipt/:transactionId → Receipt for specific transaction
|
||||||
|
```
|
||||||
|
|
||||||
|
### Onboarding (full-screen, no shell)
|
||||||
|
|
||||||
|
```
|
||||||
|
/onboarding/landing → Welcome landing page
|
||||||
|
/onboarding/phone → Phone number entry
|
||||||
|
/onboarding/verify → OTP verification
|
||||||
|
/onboarding/login → Sign-in (Google OAuth for web)
|
||||||
|
/onboarding/complete → Onboarding complete / redirect
|
||||||
|
```
|
||||||
|
|
||||||
|
### Public
|
||||||
|
|
||||||
|
```
|
||||||
|
/splash → Animated splash screen
|
||||||
|
/delete-account → GDPR account deletion request
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Key User Journeys
|
||||||
|
|
||||||
|
### 8.1 First-Time User — Pay Airtime
|
||||||
|
|
||||||
|
1. Opens app → splash animation plays.
|
||||||
|
2. (Guest) Assigned a transient workspace → lands on Home.
|
||||||
|
3. Taps **Econet** from the provider grid.
|
||||||
|
4. Enters recipient's Econet phone number.
|
||||||
|
5. Selects an airtime bundle product (e.g. $5 Airtime).
|
||||||
|
6. Chooses **EcoCash** as the payment processor.
|
||||||
|
7. Enters their own EcoCash number (the debit phone).
|
||||||
|
8. Reviews the confirmation screen: amount, charges, total.
|
||||||
|
9. Confirms → Gateway opens → approves the EcoCash prompt.
|
||||||
|
10. App polls for status → "Success".
|
||||||
|
11. Billing integration updates the airtime → Receipt displayed.
|
||||||
|
12. Taps **Share** to send receipt via WhatsApp.
|
||||||
|
|
||||||
|
### 8.2 Returning User — Repeat Transaction
|
||||||
|
|
||||||
|
1. Opens app → session is persisted → lands on Home.
|
||||||
|
2. Scrolls to **Recent Activity**.
|
||||||
|
3. Finds a past Econet airtime transaction, taps **Repeat**.
|
||||||
|
4. All fields are pre-filled → confirms without re-entering data.
|
||||||
|
5. Completes the gateway flow → Receipt shown.
|
||||||
|
|
||||||
|
### 8.3 Business User — Batch Payment
|
||||||
|
|
||||||
|
1. Signs in with Google → selects their organisation workspace.
|
||||||
|
2. Navigates to **Groups** via the nav bar.
|
||||||
|
3. Taps **+** → **Create from File** → uploads a CSV of 50 recipients.
|
||||||
|
4. Opens the group → taps **Create Batch**.
|
||||||
|
5. Reviews the batch summary (50 items, total amount).
|
||||||
|
6. Confirms → batch is submitted → each item processed individually.
|
||||||
|
7. Monitors batch progress on the batch detail screen.
|
||||||
|
8. All successes / failures are visible per item.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Responsive Design & Accessibility
|
||||||
|
|
||||||
|
- **Mobile (< 768px):** Bottom NavigationBar, single-column layout, SliverAppBar with the Velocity logo.
|
||||||
|
- **Tablet / Desktop (≥ 768px):** Side NavigationRail (256px wide, always visible), constrained content width (600px max), footer links with About/FAQs/Privacy/Terms.
|
||||||
|
- All interactive elements meet minimum touch target sizes (48×48dp).
|
||||||
|
- Skeleton loading placeholders are used during data fetches to reduce perceived latency.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Security & Compliance
|
||||||
|
|
||||||
|
- All API communication is over HTTPS.
|
||||||
|
- Auth tokens are persisted in platform-secure local storage.
|
||||||
|
- The Google OAuth client ID is a public value — no secrets are embedded in client bundles.
|
||||||
|
- Payment gateway integration uses Mastercard's hosted checkout; Velocity Pay never handles raw card numbers.
|
||||||
|
- GDPR compliance: Users can request account deletion via a dedicated in-app flow.
|
||||||
|
- Environment configuration uses compile-time flags rather than runtime `.env` files, preventing secrets from leaking into web bundles.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Upcoming / Roadmap
|
||||||
|
|
||||||
|
| Feature | Status | Target |
|
||||||
|
|---------|--------|--------|
|
||||||
|
| Push notifications for transaction status | Planned | Q2 2026 |
|
||||||
|
| Dark mode support | Planned | Q2 2026 |
|
||||||
|
| Multi-currency wallet top-up | Planned | Q3 2026 |
|
||||||
|
| Desktop app distribution (App Store / Microsoft Store) | Planned | Q3 2026 |
|
||||||
|
| Offline transaction queuing | Under Review | TBD |
|
||||||
|
| ZWL (ZiG) full currency support | In Progress | Q2 2026 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. Glossary
|
||||||
|
|
||||||
|
| Term | Definition |
|
||||||
|
|------|-----------|
|
||||||
|
| **Bill Provider** | A utility company or service provider whose bills can be paid via Velocity Pay (e.g. ZESA, Econet). |
|
||||||
|
| **Payment Processor** | The financial rail used to move funds (EcoCash mobile money, Mastercard/Visa card, in-app Wallet). |
|
||||||
|
| **Workspace** | An organisational context that groups billing accounts, transaction history, and recipient groups. |
|
||||||
|
| **Group** | A collection of recipients for batch payment processing. |
|
||||||
|
| **Batch** | A discrete payment run executed against a group of recipients. |
|
||||||
|
| **Gateway** | The Mastercard-hosted payment checkout page where card details are entered. |
|
||||||
|
| **Polling** | The process of repeatedly checking the backend for a transaction's final status after gateway authorisation. |
|
||||||
|
| **Integration** | The backend step that posts a confirmed payment to the bill provider's system (e.g. crediting a meter). |
|
||||||
|
| **MPGS** | Mastercard Payment Gateway Services — the card payment processor. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*For technical setup instructions, environment configuration, and build commands, refer to the project's [README.md](../README.md).*
|
||||||
@@ -57,6 +57,8 @@ class GroupBatchItem {
|
|||||||
final String? status;
|
final String? status;
|
||||||
final String? transactionId;
|
final String? transactionId;
|
||||||
final String? errorMessage;
|
final String? errorMessage;
|
||||||
|
final String? confirmError;
|
||||||
|
final String? integrationError;
|
||||||
final String? createdAt;
|
final String? createdAt;
|
||||||
final String? billName;
|
final String? billName;
|
||||||
final String? billProductName;
|
final String? billProductName;
|
||||||
@@ -80,6 +82,8 @@ class GroupBatchItem {
|
|||||||
this.providerLabel,
|
this.providerLabel,
|
||||||
this.providerImage,
|
this.providerImage,
|
||||||
this.creditAddress,
|
this.creditAddress,
|
||||||
|
this.confirmError,
|
||||||
|
this.integrationError,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory GroupBatchItem.fromJson(Map<String, dynamic> json) =>
|
factory GroupBatchItem.fromJson(Map<String, dynamic> json) =>
|
||||||
|
|||||||
@@ -62,6 +62,8 @@ GroupBatchItem _$GroupBatchItemFromJson(Map<String, dynamic> json) =>
|
|||||||
providerLabel: json['providerLabel'] as String?,
|
providerLabel: json['providerLabel'] as String?,
|
||||||
providerImage: json['providerImage'] as String?,
|
providerImage: json['providerImage'] as String?,
|
||||||
creditAddress: json['creditAddress'] as String?,
|
creditAddress: json['creditAddress'] as String?,
|
||||||
|
confirmError: json['confirmError'] as String?,
|
||||||
|
integrationError: json['integrationError'] as String?,
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$GroupBatchItemToJson(GroupBatchItem instance) =>
|
Map<String, dynamic> _$GroupBatchItemToJson(GroupBatchItem instance) =>
|
||||||
@@ -75,6 +77,8 @@ Map<String, dynamic> _$GroupBatchItemToJson(GroupBatchItem instance) =>
|
|||||||
'status': instance.status,
|
'status': instance.status,
|
||||||
'transactionId': instance.transactionId,
|
'transactionId': instance.transactionId,
|
||||||
'errorMessage': instance.errorMessage,
|
'errorMessage': instance.errorMessage,
|
||||||
|
'confirmError': instance.confirmError,
|
||||||
|
'integrationError': instance.integrationError,
|
||||||
'createdAt': instance.createdAt,
|
'createdAt': instance.createdAt,
|
||||||
'billName': instance.billName,
|
'billName': instance.billName,
|
||||||
'billProductName': instance.billProductName,
|
'billProductName': instance.billProductName,
|
||||||
|
|||||||
@@ -2994,6 +2994,8 @@ class _BatchDetailScreenState extends State<BatchDetailScreen>
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
Text(item.creditAddress ?? ''),
|
Text(item.creditAddress ?? ''),
|
||||||
|
Text(item.confirmError ?? ''),
|
||||||
|
Text(item.integrationError ?? ''),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user