Use server-authoritative, event-triggered SWR revalidation. Do not add timers,
refreshInterval, client payment-deadline logic, or mobile calls to ePayco.
The product API owns payment settlement: signed ePayco confirmation is normal authority; it permits one bounded stale-PSE reference validation after 20 pending minutes and explicitly forbids periodic provider polling [1]. Mobile only reads Nursy API resources after an app event.
The server must return the service and payment identifiers in assignment and
PSE-creation responses, return bank_url for a pending PSE attempt, and send
payment_deadline_at plus terminal payment/service states [1]. PSE bank return
and OneSignal payloads must contain only opaque routing identifiers:
nursy://payment-return?service_id=<id>&payment_id=<id>
{ "event": "payment.updated", "service_id": "<id>", "payment_id": "<id>" }
Neither input grants a status transition. Never put an ePayco state, amount, card data, PSE bank data, or personally identifying information in the URL or push payload. The next Nursy API read remains authority.
- After PSE creation, open server-provided
bank_url; persist local pending context only for routing, not payment status. - Configure ePayco's return target as
nursy://payment-returnwith opaque identifiers. Existing app config already declaresnursyscheme [2]. - Add an Expo Router return route. Expo Router handles routes from incoming
links; use
+native-intentonly if provider URL must be rewritten. That API runs outside app/auth context, so it should only normalize path safely [3]. - On return-route mount, call SWR
mutateonce for that service/payment cache key, then replace route with payment/service detail. If API remainspending, render pending and wait for a later server event or user refresh. Do not retry or schedule a follow-up fetch.
This handles cold launch and foreground return via same route. A browser back without deep link does not claim settlement; retain pending state and expose manual refresh.
Assignment response is immediate server truth. Write it into relevant SWR
cache entries or call one mutate per affected service and payment key before
navigating. For PSE, route to pending-payment UI after receiving bank_url.
For cash/card, route from returned authoritative service/payment state.
Mobile already exposes payment data through SWR at
/account_patient/payments; hooks return each hook's mutate [4]. There is
no SWRConfig, refreshInterval, or React Native foreground revalidation
currently configured [5]. Add a narrowly scoped revalidation helper at
implementation time rather than a global focus handler. SWR supports
programmatic mutate for one key or a key matcher [6]; prefer exact keys to
avoid refreshing unrelated account data.
Server sends a OneSignal notification after its own payment/service transition
(approved, declined, failed, reminder, or canceled_unpaid) [1].
- Register one root-level OneSignal
clicklistener. - Validate payload shape, navigate to service/payment detail, then revalidate affected exact SWR keys once.
- When a relevant notification arrives foreground, revalidate same keys once; normal display behavior stays unchanged.
- Remove listener with same callback during cleanup. OneSignal click listeners are additive and require paired removal [7].
Current onNotificationOpened creates an anonymous listener and returns no
unsubscribe; it is not suitable as lifecycle owner without adjustment [8].
| Trigger | Navigation | Refresh | Prohibited |
|---|---|---|---|
| Assignment response | Payment/service destination | Exact affected service/payment keys once | Timer; provider query |
| PSE deep-link return | Return route, then detail | Exact service/payment keys once | Trust URL status; retry loop |
| OneSignal click | Detail route | Exact service/payment keys once | Trust push status |
| Relevant foreground push | Current UI remains | Exact service/payment keys once | Global account revalidation |
| Browser back/no return link | Pending UI remains | User-triggered refresh only | Infer success; background polling |
- No client-side ePayco request, polling interval, timeout retry, or payment deadline clock.
- No client mutation of payment/service status from deep-link or push data.
- No need for
+native-intentif return URL maps directly to Expo Router route. - Do not use broad
mutate(() => true)or a global app-active refresh. Payment identifiers make targeted invalidation cheaper and deterministic. - Implement listeners and cache wiring under a separate mobile task, with tests for cold link, warm link, notification click, foreground notification, duplicate events, and pending-after-return.
- Post-assignment payment lifecycle, product API issue #111 - server authority, signed confirmation, one 20-minute PSE reconciliation, no polling, payload contract.
- Mobile
app.jsonat researched revision -nursyapplication scheme. - Expo Router: Customizing links - routing incoming links and native-only
+native-intentlimits. - Mobile
usePaymentshook at researched revision and payment endpoint - current SWR key and per-hookmutate. - Mobile root layout at researched revision - no SWR provider; repository search on 2026-08-05 found no
SWRConfig,refreshInterval,revalidateOnFocus, orAppStateconfiguration. - SWR: global mutation - programmatic single-key and matcher revalidation.
- OneSignal React Native notifications API - click listener and paired removal API.
- Mobile
useNotificationshook at researched revision - existing click-listener wrapper and subscription lifecycle.