/superscribe

A consistent way to manage App Store subscriptions

Primary LanguageGoMIT LicenseMIT

Superscribe

Build Status GoDoc

An easier way to handle App Store subscriptions

Overview

Getting App Store Status Update Notifications (now Server-to-Server Notifications) for in-app subscriptions can be tricky. RENEWAL notifications may not occur as expected, CANCEL does not indicate when auto_renew_status was switched on or off, and it can seem arbitrary when you get older iOS 6 style of receipts or the new style.

Others have described these challenges too:

Superscribe intends to provide a basic “just works”, correct solution for the main subscription use cases.

Get started

Install

go get github.com/carpenterscode/superscribe

Configure

Superscribe provides a server that both

  • scans for expiring subscriptions to check for state changes (like successful renewals) and
  • listens for App Store notifications for a limited number of subscription events.

The server needs

  1. Your App Store shared secret
  2. A func you define to retrieve expiring subscriptions from the database, called during a scan operation
  3. A listener to update the database after the scan

See how to connect listeners to Superscribe in example/main.go. This shows how to use the included AppsFlyer listener that attributes events using server-to-server API.

Optionally you can provide

  • More listeners, such as for server-side conversion analytics, etc.
srv.AddListener(AnalyticsListener{db, tracker})
  • HTTP server request handlers, such as for 200 OK responses to /healthz pings
srv.HandleFunc("/healthz", func(writer http.ResponseWriter, req *http.Request) {
	writer.Write([]byte("OK"))
})

You cannot currently

  • Modify the App Store Status Update Notification endpoint. It's currently hardcoded to /superscribe, but we can change that in the future.
  • Use anything more sophisticated than a Go time.Ticker.

Usage

Run automated tests

Generate mocks first

go generate

Test

go test ./... ./receipt

Caveats

Currently, Superscribe should only be run in a single instance setup. I personally run it on production in a single-pod Kubernetes deployment, but we should figure out how to solve for redundancy and performance by adding some kind of scalability.

Future work

There's a lot of unfortunate complexity to subscription management, so the longer term goal is to increase extensibility and robustness.

Most important: Let’s gather real use-cases and requirements to draft a prioritized roadmap.

  • Distinguish among first, first year's worth of, and remaining payments. The paid at event could be made more versatile and track 30% vs 15% App Store fee. Or to filter out renewals from first payments.
  • Track plan upgrade responses from customers. For instance, moving all monthly subscriptions from 7.99/mo to 9.99/mo.
  • Offer a scalable solution. Subscriptions in the local database should only be scanned by a single process, but multiple instances of listeners should be able to coexist. The current 1:1 model limits Superscribe to one instance.