Inspiration
What it does
How we built it
Challenges we ran into
Accomplishments that we're proud of
What we learned
What's next for Momentarium
Inspiration
Wedding photographers capture the formal moments, but guests capture everything around them: the preparations, spontaneous reactions, dance-floor chaos, and small moments the couple never sees.
Collecting those photos afterward is surprisingly difficult. They end up scattered across messaging apps, social networks, and individual phones. Asking every guest to install an app or create an account adds friction at exactly the wrong moment.
Momentarium was built around a simpler idea: guests should be able to scan one QR code and contribute photos immediately, while the couple keeps control of what becomes part of their final gallery.
What it does
Momentarium is a private, guest-powered wedding photo experience.
The couple creates and manages their wedding through the Android or iOS organizer app. Momentarium generates a reusable invitation link and QR code that can be printed or shared with guests.
Guests scan the code and open a private wedding page in their browser. They do not need to install an app or create an account. From there, they can select photos, preview them, follow upload progress, and retry failed uploads.
Each photo appears in the organizers’ review queue in real time. Organizers can approve it, decline it, or leave it for later. Approved photos become part of the wedding gallery and can be saved individually or exported according to the event’s package.
Privacy is part of the product rather than an optional setting. A wedding’s clean URL does not grant access or reveal whether an event exists. The invitation credential is validated by the backend, exchanged for a short-lived session limited to one wedding, and then removed from the visible URL.
How we built it
Momentarium uses Compose Multiplatform to share the organizer experience between Android and iOS. The guest application is also written with Compose Multiplatform and compiled to Kotlin/Wasm, allowing guests to use the experience directly in a modern browser.
Supabase provides authentication, Postgres, private Storage, Realtime updates, and Edge Functions. The guest upload flow uses narrowly scoped sessions and private storage operations rather than a public photo bucket.
The complete flow is:
- A guest scans the wedding QR code.
- An Edge Function validates the invitation and creates an event-scoped guest session.
- The browser requests a scoped upload operation.
- The photo is uploaded to private storage.
- A database record is completed only after the upload succeeds.
- Supabase Realtime delivers the new photo to the organizer app.
- The organizer’s review decision is persisted and synchronized.
RevenueCat integration
Momentarium sells access as a one-time event pass rather than a recurring subscription. A wedding has a natural beginning and end, so couples can choose Free, Essential, Classic, or Signature based on photo capacity, upload duration, retention, organizer seats, and export features.
RevenueCat’s Kotlin Multiplatform SDK powers purchases on Android and iOS. Offerings provide the available packages and localized store prices, while the authenticated Supabase user ID is also used as the RevenueCat App User ID.
The mobile client is deliberately not trusted to grant paid access. After a store purchase:
- RevenueCat sends an HMAC-signed webhook to a Supabase Edge Function.
- The server verifies the signature, application, environment, product, and transaction.
- The webhook is stored idempotently and creates a single-use purchase credit.
- An atomic database function redeems that credit for one selected wedding.
- The event receives an immutable package snapshot containing the purchased limits.
This makes every purchase event-scoped. Buying a pass for one wedding does not silently unlock every future wedding on the organizer’s account.
Momentarium has launched on iOS. The Android purchase path has also been verified end to end on a real device through Google Play internal testing: purchase sheet, RevenueCat transaction, signed webhook, purchase credit, package redemption, and event activation. The Android release is currently undergoing Google Play production review.
Purchases are intentionally excluded from the guest web application.
Challenges we faced
The first major challenge was combining convenient wedding URLs with meaningful privacy. A readable URL is useful on invitations, but it cannot be treated as a password. Momentarium therefore separates the public route from the secret invitation credential and avoids exposing event names, dates, photos, or even event existence before validation.
Billing introduced a different distributed-systems challenge. A successful store sheet does not mean the backend has already received and verified the RevenueCat webhook. Momentarium represents that delay honestly with a “Finishing your purchase…” state and briefly retries package redemption while waiting for the verified credit.
During real Android testing, the purchase completed but the event stayed locked. RevenueCat showed that the transaction was attributed to the correct user, which narrowed the failure to the webhook. The backend required an HMAC signature, but signing had not been enabled for the RevenueCat integration. Enabling signing and retrying the webhook completed the entire flow. That test turned a configuration mismatch into a verified purchase path.
Cross-platform product configuration was another challenge. Store product identifiers, offerings, build versions, and review requirements must all align across RevenueCat, Google Play, App Store Connect, Android, and iOS. Keeping product mapping behind small platform-specific boundaries allowed the rest of the purchase experience to remain shared.
What we learned
The most important billing lesson was that purchasing is not a single callback. It is a chain involving the store, RevenueCat, webhook delivery, server verification, credit creation, and event activation. Each step needs an explicit state, an idempotency strategy, and a recoverable failure path.
The project also reinforced that privacy comes from limiting information and authority. Invitation tokens are stored as secure hashes, guest sessions are short-lived and wedding-scoped, storage is private, and service credentials never enter a client application.
Compose Multiplatform worked well for sharing product behavior and visual language, but the strongest architecture was not “share everything.” Store purchases, file handling, and platform lifecycle behavior remain explicit platform boundaries around shared models, screens, and business rules.
Accomplishments
- Launched Momentarium on iOS.
- Submitted the Android release for Google Play production review.
- Built a shared Android and iOS organizer application.
- Built a guest-only Kotlin/Wasm application requiring no installation or guest account.
- Completed the private invitation, upload, database, Realtime, review, and gallery loop.
- Implemented four event packages with server-enforced limits.
- Integrated RevenueCat’s Kotlin Multiplatform SDK and store-localized pricing.
- Built an HMAC-verified, idempotent RevenueCat webhook pipeline.
- Verified a real Android purchase from the store sheet through to an unlocked wedding.
- Kept purchases and billing credentials completely outside the guest web application.
- Localized the organizer experience in Macedonian, English, Albanian, and Turkish.
What’s next
The immediate next step is completing the Android production rollout once its Google Play review is approved. From there, the focus will be monitoring the live purchase and webhook pipeline, strengthening purchase restoration, and adding scheduled retention warnings and cleanup.
Momentarium can then expand from collecting photos during the wedding to securely sharing the final approved gallery, while preserving the same principle that shaped the first version: effortless for guests, controlled by the couple, and private by design.
Built With
- android
- compose-multiplatform
- google-play-billing
- gradle
- ios
- jetpack-compose
- kotlin
- kotlin-coroutines
- kotlin-multiplatform
- kotlin/wasm
- ktor
- material-3
- postgresql
- qr
- revenuecat
- storekit
- supabase
- supabase-auth
- supabase-edge-functions
- supabase-realtime
- supabase-storage
- webassembly
Log in or sign up for Devpost to join the conversation.