Mobile Developer Guide — Golan Sertifikasi
Stack: FlutterRepository:
golan-mobilePlatforms: Android and iOS
Backend: shared versioned REST/JSON public API through KrakenD Community Edition (
golan-backend)
The mobile application is a consumer of the same backend used by the Angular web application. Business rules must remain consistent across both clients.
1. Mobile Scope
Flutter mobile is student-facing only for MVP. MVP capabilities include:- Google login/session handling and onboarding.
- Catalog/course detail.
- Midtrans top-up return/status, wallet balance and wallet-only cart/checkout.
- Order/top-up/ledger history and wallet-refund request/status.
- Permanent regular-course access, learning, progress, quiz, assignment, and regular certificate.
- BNSP registration/fulfillment status and secure LSP access display.
- In-app notifications and push notifications.
1.1 Public Client Architecture
Flutter is a public API client. Production traffic follows:- call internal gRPC endpoints;
- connect to RabbitMQ, PostgreSQL, Dragonfly, ClamAV, or internal service ports;
- discover microservices or choose service hosts;
- depend on protobuf or private database schemas;
- embed S3/SMTP/Midtrans server credentials;
- bypass KrakenD in production.
/api/v1/.... Internal gRPC/protobuf and RabbitMQ event contracts are backend-only. A backend service may be redeployed, replicated, split, or merged without changing the mobile architecture as long as the public contract remains compatible.
2. Recommended Project Structure
Feature-first structure:3. State Management
Choose one consistent state-management approach across the app. Reasonable options include:4. Networking
Use one centralized HTTP client whose base URL points to KrakenD/public API, not individual services. Responsibilities:- Base URL configuration.
- Auth/session headers or cookies according to the public contract.
- Token refresh coordination if applicable.
- Request/correlation ID propagation when supported.
- Standard success/error envelope decoding.
- Network timeout.
- Safe retry policy.
- Sensitive-data redaction in diagnostics.
- Branch on stable
error.code, not message text. - Preserve
request_idin redacted diagnostics/support context. - Treat timeouts/
502/503as potentially unknown outcomes for mutations. - Do not automatically retry non-idempotent checkout/payment/refund/submission mutations unless the public endpoint explicitly supports idempotency or provides a safe recovery contract.
- Never expose raw gateway payloads or internal stack traces to the user.
- Do not add a second network stack for direct microservice calls.
5. Authentication
Google login should use the appropriate platform-supported OAuth/Google Sign-In integration. Flow:Secure storage
Sensitive session/refresh credentials must use platform secure storage. Do not store long-lived authentication tokens in plain shared preferences.6. App Startup
Suggested bootstrap:7. Routing
Suggested routes:8. Onboarding
Same business flow as web:- Profile.
- Interests.
- Acquisition source.
- Persist temporary form state only as needed.
- Backend determines completion.
- Use course category API for interests.
- Use acquisition source API for source options.
- Do not maintain a separate mobile-only taxonomy.
9. Catalog
Course cards must visibly distinguish:- Golan handles registration/payment.
- LSP handles the official certification process.
- BNSP certificate is not issued by Golan.
10. Course Detail
Regular
Show:BNSP
Show:11. Cart and Checkout
Backend is canonical for final:12. Midtrans Top-up and Wallet Checkout
Implementation depends on the selected Midtrans mobile integration mode, but the business flow is fixed:13. Deep Link / App Return Handling
When payment returns to the application:- Parse only expected navigation data.
- Do not accept a client-side
paid=true. - Open top-up status screen.
- Refetch top-up and wallet from backend before offering wallet checkout.
- Refetch any subsequent Commerce order independently and render canonical status.
14. Regular Learning
Course player should support:15. Video
For video lessons:- Use a maintained player package.
- Support portrait/landscape.
- Resume position only if product requirements need it.
- Do not equate video playback progress alone with authoritative course completion unless backend rules explicitly define it.
16. Quiz
Requirements:- Render supported question types, including essay.
- Preserve current attempt state reasonably without making local storage authoritative.
- Submit through backend with double-submit protection.
- Backend calculates/validates objective scores and owns attempt status.
- Do not ship correct-answer flags in pre-submit payloads.
- Essay-containing attempts may become
pending_review; successful submission is not equivalent to passing. - Final result after manual grading is fetched from backend.
17. Assignment
Support:- MVP product default allows up to three submissions, but Flutter must not hardcode
3as business authority. - Render effective
max_submissions,submissions_used,can_resubmit, and reason/deadline fields returned by backend. - Each resubmission is a new server record; do not overwrite prior history locally.
- A changed policy between screen render and submit must be handled as a normal server rejection/reconciliation path.
17.1 Assignment/Media Upload Scan Contract
Uploaded assignment files are governed by Media Service and S3-compatible storage behind the public API. A completed byte upload is not proof that the file can be used. Expected lifecycle:- Only
cleanmedia is eligible to become normal trusted assignment content/download. pending_scan/scanningshould display processing state.scan_failedshould remain unavailable and expose a retry/re-upload path defined by backend.infectedmust be rejected.- Do not build or persist arbitrary S3 object keys.
- Private file access must use backend-authorized temporary access.
- Offline queues must not mark an assignment submitted until the authoritative submission endpoint confirms the record according to its contract.
18. Certificates
Only regular-course certificates are shown as Golan-issued certificates. Certificate screen:19. BNSP Registration
Student menu:24 value.
Pending UX should explain that payment is confirmed and LSP access is being prepared. When access is available, display authorized fields:
access_delivered is an authorized staff-recorded state. Student view/copy/open actions never change it. Any future student acknowledgement must be separate.
20. BNSP Credential Security
This is one of the most sensitive mobile screens. Rules:- Secret hidden by default.
- Explicit reveal action.
- Copy action should be deliberate.
- Do not take secret values into analytics.
- Do not log API body containing credentials.
- Do not persist credential responses to disk unless specifically designed and encrypted.
- Clear sensitive in-memory state when leaving screen where practical.
- Avoid screenshots if the product decides to use platform-level screenshot blocking for this screen.
- External LSP URL should require a valid HTTPS URL unless a known exception exists.
21. Notifications
MVP mobile supports:- Backend notification/event state is canonical.
- Push payloads are navigation/refetch hints.
- Duplicate push delivery must be safe.
- Register/rotate/revoke device tokens safely.
- Logout or revoked session/device policy must stop inappropriate future push association.
- Opening an old notification must fetch current backend state before rendering the destination.
22. Wishlist — Phase 2
Wishlist is Phase 2 and must not block MVP mobile release. If retained in the project structure for future compatibility, keep it isolated from core catalog/checkout dependencies. When enabled later, backend sync remains canonical and optimistic UI must roll back on definitive failure.23. Reviews — Open Product Decision
The review/rating domain exists in the broader architecture, but the current product decision has not explicitly assigned reviews to MVP or Phase 2. Do not make reviews a mobile release blocker until the SSOT assigns their phase. If enabled, backend determines review eligibility and moderation state; the app must not infer eligibility only from local purchase history.24. Error Model
Map backend error codes to domain failures. Examples:25. Offline Strategy
MVP should use a conservative approach. Reasonable offline behavior:26. Caching
Safe candidates:27. Analytics
Mobile can emit supported product events:28. Design Consistency
Mobile and web do not need pixel-identical layouts, but terminology and state naming must match. Examples: Use the same language for:29. Environment and Runtime Configuration
Typical environments:Business-policy rule
Do not ship authoritative literals such as:29.1 Distributed Backend Workflow Semantics
The backend is distributed. A client-visible action can trigger asynchronous RabbitMQ consumers after one service commits its own transaction. Mobile UX must tolerate temporary convergence gaps. Typical example:- Midtrans app/browser return is not payment proof.
- Refetch canonical order/payment state after returning to the app.
- If payment is settled while entitlement/registration is still provisioning, show a neutral processing state and refetch with bounded backoff.
- A mutation timeout means outcome may be unknown; reconcile by resource/idempotency lookup before offering an unsafe repeat.
- Do not expose RabbitMQ event names/queue timing to the UI.
- Business state comes from public API status/capability fields only.
30. Security
Minimum mobile security expectations:- Secure token storage.
- TLS-only API.
- No sensitive logs.
- No secrets in crash analytics.
- App link/deep-link validation.
- Input validation for URLs.
- Safe external browser opening.
- Certificate/LSP credential screens treated as sensitive.
- Obfuscation/minification for release where appropriate.
- Root/jailbreak checks are optional defense-in-depth, not a trust boundary.
31. Testing
Unit
Widget
Integration
Critical flows:32. Release Criteria
Before production release:- Development and staging API separated.
- Google OAuth configured for production package/bundle IDs.
- Midtrans production configuration verified.
- Deep links/app links verified.
- Crash logs checked for sensitive payload leakage.
- ProGuard/R8/Flutter release build settings reviewed.
- iOS entitlements/release signing reviewed.
- Privacy disclosures reflect collected data.
- Production API base points only to KrakenD/public REST; no internal service/gRPC endpoints are bundled.
- Upload flows have been tested through pending scan, clean, infected/rejected, and scan-failure states where applicable.
- Payment and fulfillment screens tolerate temporary asynchronous convergence without reporting false success/failure.
- Push device registration/revocation behavior is verified if push remains in the MVP release.
33. Mobile Definition of Done
A feature is complete when:- It matches backend API and SSOT statuses.
- Loading/error/offline states are covered.
- Secrets are not logged or persisted carelessly.
- Payment result waits for backend verification.
- BNSP flow is clearly external-LSP based.
- Regular certificates are the only Golan-issued certificates shown.
- Android and iOS behavior is tested.
- Core widget/unit tests are present.
- Deep links and navigation edge cases are handled where relevant.
- API traffic uses the public KrakenD/OpenAPI contract only.
- Distributed workflow state is reconciled after ambiguous timeout/network outcomes.
- Media-dependent flows wait for authoritative scan/availability state.
- No internal service topology, gRPC contract, RabbitMQ queue, or private database schema leaks into the mobile domain model.
34. Mobile Engineering Baseline
The mobile repository should commit to one predictable application architecture. Recommended baseline:35. Application State Model
Distinguish global lifecycle state from feature state. Global app state may include:36. Session Lifecycle and Refresh Concurrency
Canonical states:- Only one refresh operation should be active at a time.
- Requests waiting on refresh should resume consistently or fail consistently.
- Explicit invalid-session responses clear secure session material.
- Network timeout alone must not be interpreted as logout.
- Logout clears secure credentials and sensitive in-memory feature state.
- Never send auth tokens to analytics/crash breadcrumbs.
37. API DTO and Domain Mapping
Keep backend enums canonical. Recommended flow:unknown presentation state may be acceptable. For payment/security-sensitive state, unknown values should block consequential actions and trigger refetch/support behavior rather than guessing.
Examples of canonical enums:
38. Navigation and Deep-Link Contract
Deep links are untrusted navigation input. For each supported link:- Parse only expected path/query fields.
- Normalize/validate identifiers.
- Route through normal authentication/onboarding guards.
- Fetch canonical object state from backend.
- Do not trust status or authorization encoded in the URL.
paid, success, price, entitlement, or credential flags.
39. Mobile Mutation Safety
Each mutation should define behavior for:- Do not blindly retry non-idempotent requests.
- Use backend idempotency keys where supported.
- On ambiguous timeout, reconcile by fetching canonical state.
- Disable repeated action while the initial request is actively in flight.
- Persist only the minimum recovery identifier needed to resume a safe status check.
- Do not infer completion simply because an HTTP request returned 2xx; inspect the returned business state and refetch when needed.
40. Detailed Payment Verification UX
Recommended screen states:41. Learning Session and Progress Rules
Mobile should not assume every lesson has the same completion trigger. Potential completion triggers must follow backend/product rules:42. Quiz Attempt Reliability
An active quiz attempt needs an explicit recovery model. Recommended behavior:- Backend owns attempt number, start time, expiry, submission status, and grading state.
- Mobile may preserve unsent answers locally for UX if the data is non-sensitive and lifecycle rules permit it.
- On reopening, fetch current attempt state before submitting.
- Timer display is derived from backend attempt timing, not solely from a local countdown started when the widget mounted.
- Submission must be protected from double tap.
- A lost response after submission should trigger attempt-status reconciliation before retrying.
- Correct answers are displayed only according to backend review policy.
- Essay/manual grading must render an explicit
pending_reviewstate and later reconcile to the backend final grade/pass-fail result. - A 2xx submit response is not itself proof of pass/completion.
43. Assignment Upload and Resubmission Contract
For file assignments:- Backend still validates type, size, ownership, authorization, and effective resubmission limit.
- Temporary upload failures should be distinguishable from final submission failures.
- Avoid requesting broad storage/media permissions when a platform document picker can provide scoped access.
- Do not retain private assignment files longer than needed in temporary app storage.
- Render backend
max_submissions,submissions_used, andcan_resubmit; never use a local literal as authority. - Each successful resubmission produces a separate historical record.
44. BNSP Sensitive-Screen Lifecycle
Credential retrieval should be isolated from normal persisted repositories/caches. Recommended flow:45. Push and In-App Notification Architecture
The locked product SSOT requires push notification in MVP together with in-app and email. The current backend guide still labels FCM/push as a later Notification Service extension; backend documentation/implementation must be aligned before MVP release. Mobile must not create a separate notification authority to compensate. Architecture rules:- Notification Service/backend state is canonical.
- Push is a delivery hint; on open, navigate by safe reference and refetch the resource through KrakenD.
- Push payloads must not contain LSP passwords, auth tokens, Midtrans secrets, private signed URLs, or sensitive financial data.
- Register/rotate/revoke device tokens using public API endpoints owned by the notification architecture.
- On logout or revoked session, remove/revoke the device registration according to backend contract; do not assume local token deletion alone revokes server delivery.
- Duplicate push delivery must not duplicate business mutations.
- In-app notification read state is server-owned when the API exposes it.
- Do not require WebSocket for MVP correctness. Push + persisted in-app state + refetch is sufficient for awareness; live bidirectional sockets are not part of the initial backend architecture.
46. Offline and Local Persistence Matrix
Classify data before caching it.Safe or relatively safe to cache
Cache with caution
Do not persist by default
47. Connectivity and Error Recovery
Connectivity APIs are hints, not proof that a request will succeed. Error categories should distinguish:noNetwork→ allow safe retry when connectivity may return.401after failed refresh → return to login.403→ access denied; do not retry indefinitely.422/validation→ preserve inputs and show field/domain errors.409/conflict→ refetch canonical state where appropriate.5xx→ show recoverable failure and correlation/request ID when support value exists.
48. Mobile Observability and Sensitive-Data Redaction
Crash/error telemetry should include only data useful for diagnosis. Safe examples:49. Platform Lifecycle Handling
Test important flows when the application:50. Mobile Design-System Contract
Create shared widgets/tokens for:- Touch targets must be practical for mobile use.
- Text scaling should not break critical actions.
- Icons used as buttons need semantic labels.
- State cannot rely on color alone.
- Forms should use correct keyboard/input types where possible.
51. Expanded Mobile Test Matrix
Authentication
Commerce + refund
Learning
BNSP
Notifications
Lifecycle
Platform
Run critical integration flows on both Android and iOS; emulator-only testing is not sufficient for Google sign-in, deep links, secure storage, payment returns, and notification behavior.52. Build and Release Pipeline
Minimum CI stages:53. Mobile Feature Acceptance Template
54. Refund Workflow
Refund is MVP and is manually reviewed by admin/backoffice through the web application. Student mobile flow:- Current default request window is seven days after payment, but mobile must not calculate
paid_at + 7 daysas business authority. - Prefer backend fields
can_request_refund,refund_request_deadline_at, and reason codes. - Request submission does not mean approval or completed refund.
- Do not revoke local course access or hide BNSP data based solely on the presence of a refund request.
- Delivered BNSP access is non-refundable by default; show the backend decision/reason rather than duplicating the policy.
- Handle ambiguous network response by reconciling existing request/order state before retrying.
55. Config-Driven Business Policy
Flutter may cache non-sensitive, server-provided effective values for presentation, but it must not own business configuration. Examples of backend-derived values:56. MVP Scope and Open Product Decisions
Flutter MVP remains student-only. Administrative/backoffice features must not leak into the mobile release scope. Explicit Phase 2 items are non-blocking:Open Product Decision list. Current examples include significant-course-usage refund threshold, partial-refund policy, BNSP business-calendar mechanics, review release phase, coupon release phase, optional student BNSP acknowledgement, and certificate reissue policy.