storebound/docs/research/fastlane-revenuecat.md

6.6 KiB

R&D: a fastlane importer and a RevenueCat adapter

Two questions for after desktop D3. Facts come from the official docs linked in each section; the recommendations are ours.

1. storebound import fastlane

What fastlane keeps on disk

  • deliver (App Store), under fastlane/metadata/ (docs.fastlane.tools/actions/deliver):
    • One folder per language: name.txt, subtitle.txt, description.txt, keywords.txt, promotional_text.txt, release_notes.txt, support_url.txt, marketing_url.txt, privacy_url.txt.
    • Non-localized files at the root: copyright.txt, primary_category.txt, secondary_category.txt and the sub-category files.
    • review_information/: first_name.txt, last_name.txt, phone_number.txt, email_address.txt, demo_user.txt, demo_password.txt, notes.txt.
    • A default/ folder that fills any language without its own files.
    • Screenshots and app previews in one folder per language. The device comes from the image size or the file name.
  • supply (Google Play), under fastlane/metadata/android/<locale>/ (docs.fastlane.tools/actions/supply):
    • title.txt, short_description.txt, full_description.txt, video.txt.
    • changelogs/<versionCode>.txt and changelogs/default.txt.
    • images/: icon, featureGraphic, promoGraphic, tvBanner, plus phoneScreenshots/, sevenInchScreenshots/ and tenInchScreenshots/.
  • Appfile / Deliverfile / Fastfile are Ruby:
    • app_identifier, apple_id, team_id / itc_team_id, json_key_file, package_name.
    • Deliverfile options such as languages, submit_for_review, automatic_release, phased_release, price_tier, app_rating_config_path.

What an importer would do. It would only read, it would never run Ruby, and it would write with --dry first.

  1. Store ids. Parse the simple key("value") / key "value" lines of Appfile and Deliverfile with a strict regex. That gives apple.bundleId, play.packageName and the languages. Ruby logic (conditionals, ENV[...], lanes) is reported as "set it in storebound.config.ts", never guessed.
  2. Languages. Join the deliver locales and the supply locales into storebound languages[] (apple, play codes).
  3. Listing text.
    • Write store/appstore.md and store/listing.md from the .txt files, mapping default/ into every language that lacks a file.
    • The newest changelogs/<versionCode>.txt becomes whatsNew.
    • Check lengths against the store limits (storebound check).
  4. Art. Copy or link the screenshot folders into the renders layout (per language and size), or list them as shots.frames when they look like raw captures. The supply icon and feature graphic go to the configured paths.
  5. Secrets and personal data.
    • json_key_file and review_information/* (contact, demo user and password) go to .storebound/credentials.json, which is gitignored, and never to the config (AGENTS.md rules 4 and 9).
    • The importer prints that it moved them, masked.
  6. Not imported, with a note:
    • Submission and release flags (submit_for_review, automatic_release). storebound never submits.
    • price_tier. Prices live in the products / CSV flow.
    • The age rating config (answered with the owner in store-declarations).
    • Fastfile lanes.

Worth it? Yes. fastlane is the most common existing setup, and its metadata folder maps almost one to one onto storebound's listing files. A read-only importer gives a migration path that keeps the store text, the screenshots and the credentials layout in a few seconds, and a dry run shows exactly what lands where.

Effort: about 2 to 3 days with tests on fixture folders: an Appfile / Deliverfile line parser, the two metadata walkers, a dry-run report, and the .storebound/ hand-off. Ship it as storebound import fastlane [--dir fastlane] [--dry], then as an "Import from fastlane" choice in the desktop first-run guide.

2. A RevenueCat adapter

What the API offers. RevenueCat REST API v2 lives at https://api.revenuecat.com/v2. It uses Bearer v2 secret keys with per-endpoint permissions, set in the project settings (revenuecat.com/docs/api-v2). It covers:

  • Apps.
  • Products: /projects/{project_id}/products.
  • Entitlements, with .../entitlements/{entitlement_id}/actions/attach_products.
  • Offerings and Packages.

So the RevenueCat catalog can be created and kept in sync by API.

What storebound already replaces. The store-side catalog: one product and subscription list in the config, created and priced on both stores (play products / subscriptions, apple iap / subscriptions, the regional price CSV), with cross-store parity (catalog diff). Teams that use RevenueCat only as a way to "set up the products" don't need it for that.

What storebound cannot replace.

  • Runtime receipt and purchase-token validation.
  • Entitlement state per user, server notifications (App Store Server Notifications, Play RTDN).
  • Renewals, grace periods, refunds, customer history, paywall experiments and charts.
  • The client SDKs.

All of that runs on a server, against live users. It is outside a release tool, and storebound should not try.

What an optional adapter could do: storebound revenuecat sync [--dry].

  • Key: REVENUECAT_API_KEY (v2 secret) and project id, from the environment or .storebound/ only.
  • Source of truth: the storebound catalog. For every config product or subscription id and each store:
    1. Create the RevenueCat product (store identifier, app id, type).
    2. Create the entitlements named in a new optional config field (products[].entitlements: ['pro']) and attach the products.
    3. Optionally create one default offering whose packages map to the subscriptions ($rc_monthly / $rc_annual by period).
  • Idempotent: match by store identifier. A dry-run diff comes first, as with every storebound write. Nothing is deleted.
  • Read-only parity: show "missing in RevenueCat" in catalog diff and in the desktop Catalog tab.

Recommendation. Build the fastlane importer first: it is the migration path and the bigger win.

Make RevenueCat a small, optional adapter later, scoped to catalog sync (products, entitlements, a default offering) plus parity in catalog diff. Market it as "keep RevenueCat in step with the stores", not as a replacement:

  • storebound owns the store-side catalog and the release.
  • RevenueCat (or the app's own server) owns runtime entitlements.

Do not add webhooks, customers or any runtime data, so storebound stays a local tool with no server and no user data.