Purchases & Effects
What a native funnel session yields — effects, the store purchase path, attributes, dismissal, and resume.
A running session answers every interaction with a step: the screen to draw
plus the effects the host must act on. The effect vocabulary lives in one
place — FunnelEffect in librovenue.udl
(packages/core-rs/src/librovenue.udl:351-360): Dismiss, OpenUrl,
ShowAlert, Custom, Purchase, and RestorePurchases. Navigation,
variables and visibility are the machine's own business and produce no
effect (packages/funnel-core-rs/src/actions.rs:146-147).
Buying: purchase_selected and restore_purchases
An authored purchase_selected action means the funnel waits on the store
(packages/funnel-core-rs/src/actions.rs:126-129). It surfaces to the host
as a Purchase effect carrying the selected package id; restore_purchases
surfaces as RestorePurchases
(packages/funnel-core-rs/src/actions.rs:170-179). The host completes both
through the SDK's normal store purchase path — purchase alongside
restorePurchases on every SDK
(packages/sdk-swift/Sources/Rovenue/Rovenue.swift:835,
packages/sdk-swift/Sources/Rovenue/Rovenue.swift:888,
packages/sdk-kotlin/src/main/kotlin/dev/rovenue/sdk/Rovenue.kt:737,
packages/sdk-kotlin/src/main/kotlin/dev/rovenue/sdk/Rovenue.kt:801,
packages/sdk-rn/src/index.ts:54,
packages/sdk-flutter/rovenue_flutter/lib/src/rovenue.dart:176,
packages/sdk-flutter/rovenue_flutter/lib/src/rovenue.dart:196).
A purchase with nothing selected produces no effect at all — there is
nothing to buy, and the screen's purchase button is disabled in that state
(packages/funnel-core-rs/src/actions.rs:170-173). The purchase the host
then makes carries the funnel's attribution snapshot, so revenue lands on
the placement, funnel and variant that converted the subscriber: without it
a purchase made inside a funnel lands unattributed and nothing errors
(packages/core-rs/src/librovenue.udl:164-167).
set_attribute never reaches the host
SetAttribute is deliberately absent from the effect enum: librovenue
intercepts it and routes it into the existing attributes module, so it is
never implemented three times
(packages/core-rs/src/librovenue.udl:348-350,
packages/core-rs/src/funnel/session.rs:18). Inside the session the step
converter absorbs it — recorded for the owning core to flush through its
attributes path — while every other effect is forwarded to the host
(packages/core-rs/src/funnel/session.rs:316-322).
Ending: close_funnel and dismiss()
An authored close_funnel action means the funnel is over
(packages/funnel-core-rs/src/actions.rs:124-126); it surfaces as the
Dismiss effect (packages/funnel-core-rs/src/actions.rs:151). The host
side is dismiss(): the façade calls it when the host closes the view or
on a Dismiss effect, and it emits funnel_dismiss — once only, so a close
handler firing twice never double-counts the dismissal
(packages/core-rs/src/funnel/session.rs:421-427).
Resume: state_json restores and re-emits nothing
state_json() persists the session — machine state plus the presentation id
(packages/core-rs/src/funnel/session.rs:404-416). The host stores the
string and hands it back to open_funnel as restored_state_json
(packages/core-rs/src/librovenue.udl:373-376). A fresh open emits
funnel_view plus funnel_screen_view for the first screen; a restored
open emits nothing and keeps the stored presentation id, so the resumed
session counts once
(packages/core-rs/src/librovenue.udl:258-263,
packages/core-rs/src/funnel/session.rs:186-192).
Restore seeds the emission tracking where the restored machine stands: the
first post-restore step does not re-emit the arrival it resumes on, and an
already-finished machine does not emit a completion for a funnel that ended
before this wave (packages/core-rs/src/funnel/session.rs:237-248). A
stored session recorded against a different funnel or revision is discarded
rather than replayed, so handing back a stale one is safe
(packages/sdk-swift/Sources/Rovenue/FunnelUI/ResolvedFunnel.swift:15-17).
Garbage degrades to fresh; a pre-wave bare machine state still restores,
with a fresh id
(packages/core-rs/src/funnel/session.rs:452-466).
The device wire names are funnel_view, funnel_screen_view,
funnel_complete and funnel_dismiss — a closed list; any other
funnel_<x> stays outside the session vocabulary
(apps/api/src/lib/funnel-event-shape.ts:86-91).