Skip to main content

Mobile Developer Guide — Golan Sertifikasi

Stack: Flutter
Repository: golan-mobile
Platforms: 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.
Mobile does not implement admin, instructor, finance, customer-service, or other backoffice functions for MVP. Those operational surfaces are handled by Angular web while consuming the same backend business rules. Wallet, paid-course-completion referral reward visibility and wallet-only purchases are MVP mobile workflows; instructor withdrawal and platform payouts remain authorized web/backoffice workflows. Wishlist, discussions/community, advanced CMS, advanced analytics and growth features are non-blocking Phase 2 features. The mobile app must not become an authority for authorization, payment result, tax, refund eligibility, assessment result, course completion, certificate issuance, BNSP delivery, or notification state.

1.1 Public Client Architecture

Flutter is a public API client. Production traffic follows:
Flutter must not:
  • 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.
The canonical public API contract is OpenAPI under /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. Feature-first structure:
Within each feature:
Use this separation where it improves maintainability; do not generate excessive boilerplate for trivial features.

3. State Management

Choose one consistent state-management approach across the app. Reasonable options include:
Do not mix multiple competing state-management systems without a clear reason. Recommended state categories:
Backend remains the business source of truth.

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.
Canonical success:
Canonical error:
Rules:
  • Branch on stable error.code, not message text.
  • Preserve request_id in redacted diagnostics/support context.
  • Treat timeouts/502/503 as 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:
Do not trust local Google user data as the application user source of truth. KrakenD may reject invalid access tokens at the edge, but the owning service still enforces authorization. Local role checks are UX only.

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:
Handle offline/network failures separately from invalid authentication.

7. Routing

Suggested routes:
Use deep links where useful, especially:

8. Onboarding

Same business flow as web:
  1. Profile.
  2. Interests.
  3. Acquisition source.
Requirements:
  • 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:
BNSP messaging must state that:
  • Golan handles registration/payment.
  • LSP handles the official certification process.
  • BNSP certificate is not issued by Golan.
This message must not be hidden deep inside terms.

10. Course Detail

Regular

Show:

BNSP

Show:

11. Cart and Checkout

Backend is canonical for final:
Mobile may display estimated values while editing cart, but checkout must use backend-calculated order data.

12. Midtrans Top-up and Wallet Checkout

Implementation depends on the selected Midtrans mobile integration mode, but the business flow is fixed:
Never treat return-to-app as verified top-up credit or course payment. Course checkout never creates a Midtrans transaction.
When payment returns to the application:
  1. Parse only expected navigation data.
  2. Do not accept a client-side paid=true.
  3. Open top-up status screen.
  4. Refetch top-up and wallet from backend before offering wallet checkout.
  5. Refetch any subsequent Commerce order independently and render canonical status.
Support:

14. Regular Learning

Course player should support:
Possible offline support can be added later, but it introduces DRM/storage/sync complexity and should not be assumed in MVP. Progress updates must be sent to backend.

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.
Network interruption behavior must follow the reliability rules later in this guide.

17. Assignment

Support:
Request storage/media permission only when actually needed and according to platform policy. Rules:
  • MVP product default allows up to three submissions, but Flutter must not hardcode 3 as 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:
Rules:
  • Only clean media is eligible to become normal trusted assignment content/download.
  • pending_scan/scanning should display processing state.
  • scan_failed should remain unavailable and expose a retry/re-upload path defined by backend.
  • infected must 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:
Current status:
Regular certificates are permanent and do not have an expiration date. Do not display expiry UI or compute one locally. If opening a PDF or verification URL externally, use secure platform navigation. Do not create any UI suggesting a Golan-issued BNSP certificate. Certificate availability/status must come from backend, not local course completion state.

19. BNSP Registration

Student menu:
States:
The backend should expose fields such as:
The current product default is fulfillment within 1 × 24 hours according to the configured business-day calendar, but the app must not compute that deadline from a hardcoded 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.
Mobile cannot guarantee secrecy after user copies credentials, so backend and LSP account policy should assume the user ultimately controls the credential once delivered.

21. Notifications

MVP mobile supports:
Potential events:
Rules:
  • 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:
Do not display raw server stack/error details.

25. Offline Strategy

MVP should use a conservative approach. Reasonable offline behavior:
Do not queue blindly:

26. Caching

Safe candidates:
Avoid persistent cache for:

27. Analytics

Mobile can emit supported product events:
Never include secret values. Use backend-supported event names to keep web/mobile analytics compatible.

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:
Do not invent client-specific business statuses.

29. Environment and Runtime Configuration

Typical environments:
Mobile runtime/build configuration may include:
Never embed backend/internal values such as:
The app calls public KrakenD routes only.

Business-policy rule

Do not ship authoritative literals such as:
Use backend-derived values/capabilities for presentation, including:
The backend validates every mutation again.

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:
Mobile rules:
  • 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.
Backend authorization remains mandatory.

31. Testing

Unit

Widget

Integration

Critical flows:
Use fake/sandbox credentials only.

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:
  1. It matches backend API and SSOT statuses.
  2. Loading/error/offline states are covered.
  3. Secrets are not logged or persisted carelessly.
  4. Payment result waits for backend verification.
  5. BNSP flow is clearly external-LSP based.
  6. Regular certificates are the only Golan-issued certificates shown.
  7. Android and iOS behavior is tested.
  8. Core widget/unit tests are present.
  9. Deep links and navigation edge cases are handled where relevant.
  10. API traffic uses the public KrakenD/OpenAPI contract only.
  11. Distributed workflow state is reconciled after ambiguous timeout/network outcomes.
  12. Media-dependent flows wait for authoritative scan/availability state.
  13. 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:
Avoid mixing Riverpod, Bloc, Provider, GetX, and ad-hoc global singletons. Pick one primary state pattern and use exceptions only with documented justification. Recommended dependency direction:
Simple read-only features do not need ceremonial layers, but payment, authentication, learning submissions, and BNSP credentials should have explicit boundaries.

35. Application State Model

Distinguish global lifecycle state from feature state. Global app state may include:
Feature state belongs within the feature:
Do not create a single giant application state object that causes unrelated screens to rebuild and makes sensitive values long-lived in memory.

36. Session Lifecycle and Refresh Concurrency

Canonical states:
Startup:
Refresh requirements:
  • 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.
If the backend changes token/session strategy, mobile must follow the documented API contract rather than implementing its own assumptions.

37. API DTO and Domain Mapping

Keep backend enums canonical. Recommended flow:
Use strict parsing for required fields in sensitive flows so malformed data fails visibly during development/testing. Unknown enum strategy must be explicit. For non-critical display enums, an 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:

Deep links are untrusted navigation input. For each supported link:
  1. Parse only expected path/query fields.
  2. Normalize/validate identifiers.
  3. Route through normal authentication/onboarding guards.
  4. Fetch canonical object state from backend.
  5. Do not trust status or authorization encoded in the URL.
Examples:
Payment return links must carry only enough information to find/refetch the internal order/payment state. They must not carry trusted paid, success, price, entitlement, or credential flags.

39. Mobile Mutation Safety

Each mutation should define behavior for:
For low-risk idempotent operations:
For checkout/payment/refund/quiz submission/assignment submission:
  • 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:
After returning from Midtrans:
Important distinction:
If the app is terminated after payment and reopened, order history must still reconcile from backend without relying on previous local navigation state.

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:
If only explicit completion is supported in MVP, keep the client model simple and do not infer completion from playback percentage. Learning screen should tolerate backend content changes between sessions:
On conflict, refresh canonical course structure instead of trying to preserve stale local assumptions.

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_review state 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:
Requirements:
  • 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, and can_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:
Never place raw credentials in:
If screenshot blocking is enabled, test both Android and iOS behavior because platform capabilities differ. Clipboard behavior should be deliberate. If automatic clipboard clearing is introduced, it must be tested carefully and documented because mobile OS behavior varies.

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.
Push navigation flow:

46. Offline and Local Persistence Matrix

Classify data before caching it.

Safe or relatively safe to cache

Cache with caution

These require stale-data handling and authorization revalidation after session changes.

Do not persist by default

Offline UI must clearly label stale data where incorrect freshness could mislead the user.

47. Connectivity and Error Recovery

Connectivity APIs are hints, not proof that a request will succeed. Error categories should distinguish:
UX rules:
  • noNetwork → allow safe retry when connectivity may return.
  • 401 after 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.
Do not show raw stack traces or gateway payloads.

48. Mobile Observability and Sensitive-Data Redaction

Crash/error telemetry should include only data useful for diagnosis. Safe examples:
Redact or exclude:
Logging interceptors must default to redacting headers/body fields in production.

49. Platform Lifecycle Handling

Test important flows when the application:
On resume, refetch time-sensitive state when necessary:
Do not assume an in-memory state notifier survives OS process death.

50. Mobile Design-System Contract

Create shared widgets/tokens for:
Accessibility requirements:
  • 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.
Terminology must match web/backend canonical statuses.

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:
Release configuration must separate:
Production secrets that belong on backend must never be embedded in Flutter assets, Dart constants, native resource files, or CI-generated application bundles.

53. Mobile Feature Acceptance Template

Payment, refund, BNSP credential, authentication, manual grading/assessment result, certificate, and notification screens should not pass release review with undefined recovery semantics.

54. Refund Workflow

Refund is MVP and is manually reviewed by admin/backoffice through the web application. Student mobile flow:
Rules:
  • Current default request window is seven days after payment, but mobile must not calculate paid_at + 7 days as 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:
Values such as PPN rate, refund window, BNSP SLA, and assignment submission limit must not be duplicated as Dart constants, remote-config authority, or platform resource values. The backend validates all mutations again.

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:
For unassigned behavior, follow the SSOT 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.