Here’s Lab Surprise, a game bundle store built with just 2 Google Cloud Functions (and an integrated payment service API!).
The idea: Get a surprise bundle of 3 VR apps from 19 games (the more you reveal, the lower your discount). 19 choose 3: that’s 969 different bundles, with 4 discounts possible for each bundle 😂
VR Scout explain it nicely: https://vrscout.com/news/app-lab-bundle-major-quest-discounts/
What is it?
Mid-march, after a first successful indie VR bundle with 12 games, the idea of Lab Surprise emerged as an inclusive way of giving our now 14 very different games, in terms of price, gameplay, etc. an equal chance to be seen and bought by all players.
We couldn’t find any platform or store that would allow us to have the mix and surprise elements we wanted to have (we searched!), so… I build it over the course of a month, testing and iterating with the group 😅
Now for the dirty, shameful secret: the first version of the drawing-and-revealing custom backend was actually built with a Google Spreadsheet and an App Script. It worked quite well for the ball of strings and glue it was! Moving to Cloud Functions helped remove a lot of specific code for Sheets, in addition to making requests much faster. Apps Script is actually fun to use and the web editor itself is really nice. Sheets are also a great way to sketch your data structure very quickly and change it as you prototype. There’s even a hack/Chrome extension to version you Apps Script with GitHub. But ultimately, it’s not good enough and it feel too cumbersome compared to Cloud Functions.
Tip: use Cloud Shell if you are new to Cloud Functions, it allowed me to get going in a minute without installing anything on my local machine.
What are the parts that make it work?
A key part of the flow is Paddle and using the Paddle API in a dynamic way. Paddle is the merchant of record and manages the taxes worldwide, which would be a nightmare to manage ourselves 😱.
We are using the fact that Paddle can split a sale automatically between sellers to immediately route the money to the vendor account of each developer in a given bundle, calculating the split for each bundle. That means that we had to onboard all 19 developers into Paddle in a record time. It was a totally new use case for Paddle, and they were really open and helpful about it.
Technically, Lab Surprise is made of just 4 parts, all exchanging through web APIs and decoupled:
- The web page itself, the front (it’s not doing a lot really 😂 just updating the graphics and price on the page depending on the bundle and revealed apps)
- The drawing-and-revealing custom backend (one cloud function drawing a random surprise of 3 games, updating the reveal status of the surprise and sending back the price, discount and paylink)
- The Paddle service (Paddle does a lot here: checkout, reporting, tax remittance, emailing the keys we pass them to buyers, even first level support)
- The key fulfillment custom backend (one cloud function checking if keys are available and passing them to Paddle)
The custom backends are using a few collections and sub-collections in Google Firestore: games, promotions, keys, surprises… The Firestore API is not perfect, but simple and well documented.
There’s also a couple of other custom back-office functions: one function that handles adding a batch of keys for a given game (with a very simple form for devs to use) and one private function to generate the bundles for a promotion.
I’m very happy that I didn’t try to go the route of setting up a traditional monolithic web application.
Cloud functions force you to think in a decoupled way (even if there’s several ways that you can couple them via the data) and at this stage it allows for easier updates and explorations. For example it’s really easy to deploy just a variant of the draw function to test new features.
There’s also benefits in thinking the API-first way. I’m very senior as a creative and a prototyper, and I started programming when I was maybe 9 or 10, but I’m not a developer by trade. API-first design is helping me greatly in designing my architecture, by focusing on the relationships of the parts.
The update puzzle
This decoupling can make updates a multi-steps process: at some point after the launch of Lab Surprise, I had to update the way the game IDs were passed by the drawing function first to Paddle paylink API and then on checkout by Paddle to our fulfillment function. To update that message passing without breaking anything and without interruptions, I couldn’t just change both functions to the new message passing, as it could have led to a few buyers getting the message from the old version passed to the new version and then not getting their game keys.
I had to first make the fulfillment function accept both types of messages, old and new (and now in a more extensible way😅). Then I could create a new version of the drawing function, passing only the new message format, test it against the live, production fulfillment function, and deploy it once everything was tested. I can ultimately go back to the fulfillment function and remove the legacy code.
If both parts had been consolidated into a single app, I might have been able to update both of them in one go. But in many cases, you are not controlling the pace of updates of some parts of the system anyway, for example is you have a client app that your user download. It was an interesting update lesson, one that fortified my views on decoupled updates: we are going to have similar issues with our VR game Peco Peco, and additive updates like that might be the way to go more often than not.
The system behind Lab Surprise is build in such a way that it’s really easy to add new games in Lab Surprise along the way: it was a major frustration during the Waiting for App Lab bundle when we had to say no to developers who asked to join (because of how the Itch co-op bundle system works), so this time we wanted to be able to say yes!
We were able to add 5 more games in the middle of Lab Surprise without issues or interruptions. We just created a new promotion and switched to it.
The way we built it, it is also easy to create evolutions: we could include all paid App Lab games if their developers were interested 😃. We could add an official Quest store games for a 2-days period as a special event… We could create a version with 6 games in the surprises, or 20, or just 1.
In term of business, we have gained some autonomy in terms of experimenting with marketing our apps. And I think this is good for the whole indie VR ecosystem, as it shows that we don’t need to wait for the stores to evolve, we can build our own experiments in terms of marketing.
OK, but why build a surprise machine instead of a classic bundle store?
As a collective of indie developers, it’s really important to showcase the whole lineup of games, not just one or two games, to give players a way to explore and take a chance on games that they might not think of trying.
As our group grows, we need to find ways to reach out to players that don’t exclude or sideline one developer or the other. We don’t need to replicate what others are doing, we need to find new ways to market and sell our games that reflect our own position in the VR and indie game market.