Skip to content

Instantly share code, notes, and snippets.

@danielsilverstone-ct
Created April 11, 2022 15:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save danielsilverstone-ct/ac4791401cbdb4083409baa66a689102 to your computer and use it in GitHub Desktop.
Save danielsilverstone-ct/ac4791401cbdb4083409baa66a689102 to your computer and use it in GitHub Desktop.

Reasoning

As part of supporting donation (and purchase) for apps in Flathub, each app (and platform) needs to be able to set their minimum and recommended values, and the cut given to their dependencies.

This document is intended to lay out how this might work, along with a proposed algorithm to turn a "pay X to app Y" into a transaction which divides the funds appropriately.

Fundamentals

Each of Flathub, the platform(s) for the app, and the app, will be given the "token" amount regardless of any additional funds. For the purpose of this document we will consider the "token" amount 1 dollar.

Applications are permitted to receive donations, or to expect payment in return for being permitted to download the application.

Applications which expect payment may also define a recommended donation amount which is higher than the payment amount, though the user is under no obligation to pay that.

Data model

AppPaymentScheme

(Database, with APIs to manipulate)

  1. ID
  2. Application (String, FH appid)
  3. PaymentAmount
  4. DonationAmount
  5. PlatformCut

Platform

(JSON documents)

  1. Platform ID (e.g. org.freedesktop.Runtime)
  2. Dependency IDs (e.g. org.flathub.FlatHub)
  3. DependencyCut

Examples

org.gimp.GIMP

GIMP is a free software project, they want to permit donations. They have a runtime of org.gnome.Platform and they set their platform cut at 50%

GNOME's platform builds atop FDSDK, so their platform definition includes FDSDK as a dependency. GNOME set the platform cut at 30%

FDSDK, as a basis platform for flatpak, depends on only FlatHub itself. FDSDK set the platform cut at 50%

GIMP's minimum "token" total is therefore 4 dollars (FH, FDSDK, GNOME, GIMP).

GIMP set their recommended donation level at 10 dollars.

A user clicks to donate to GIMP, they are presented with a dialog which shows the breakdown. This is computed as:

  1. Each party takes their token amount
  2. There are 6 dollars left
  3. GIMP takes its cut
  4. There are 3 dollars left
  5. GNOME takes its cut
  6. There are 90 cents left
  7. FDSDK takes its cut
  8. There are 27 cents left
  9. FlatHub takes the rest

Giving a final breakdown of:

  • GIMP receives 4 dollars
  • GNOME receives 3.10 dollars
  • FDSDK receives 1.63 dollars
  • FlatHub receives 1.27 dollars

The user is then permitted to shift the donation down to 4 or up to whatever they want, and the computation is rerun at whatever donation level they choose.

com.publisher.Game

Game, by Publisher, is a pay-for game which the user will need to pay for before they can download and run it. The publisher sets the purchase price for the game at 15 dollars. The game uses the Freedesktop SDK as its platform. They set the platform cut at 30%.

A user clicks to buy Game, they are presented with a dialog showing the breakdown computed like before, giving:

  • Game receives 9.40 dollars
  • FDSDK receives 3.52 dollars
  • Flathub receives 2.08 dollars

Again, the user can shift the amount but not below the purchase price of 15 dollars.

What about donations and purchasing?

An app can set a recommended donation amount as per the GIMP example, and also a purchase amount as per the Game example, but if they do:

  1. Purchase amount must be less than or equal to recommended donation amount
  2. User will be presented with the slider at the recommended amount, but will be permitted to drop it as low as the purchase amount.

APIs needed

GET /vending/{appid}

Returns the vending status for the given app ID. The app ID can be a platform ID in which case the info will be returned for the platform. This permits requesting the info for org.freedesktop.Runtime or for org.flathub.FlatHub for example.

If the given appid is not known, or any other error occurs, you will get a JSON object of the form:

{
    "status": "error",
    "error": "...",
}

If the appid is known, is permitted to receive donations / take payment, etc. then the returned data will be a JSON object of the form (using GIMP example):

{
    "status": "ok",
    "kind": "donation",
    "components": [
        "org.gimp.GIMP",
        "org.gnome.Platform",
        "org.freedesktop.Runtime",
        "org.flathub.FlatHub",
    ],
    "currency": "usd",
    "tokens": [
        100,
        100,
        100,
        100,
    ],
    "cuts": [
        "50",
        "70",
        "70",
    ],
    "minimum": 0,
    "recommended": 600,
}

The intention is that the website can use the token values and cuts to render the UI properly, the minimum must be summed with the token values, and the recommended must be summed to the computed minimum. So in the above example, GIMP don't set a minimum above token (not a payment app) so 0 becomes 400 for the token values. Recommended becomes 1000 by adding to minimum.

Note that the cuts list will be one shorter than the components list because FlatHub always consumes the remaining amount, so it would be a redundant 100.

By comparison, this is what the Game example would return:

{
    "status": "ok",
    "kind": "purchase",
    "components": [
        "com.publisher.Game",
        "org.freedesktop.Runtime",
        "org.flathub.FlatHub",
    ],
    "currency": "usd",
    "tokens": [
        100,
        100,
        100,
    ],
    "cuts": [
        70,
        80
    ],
    "minimum": 1200,
    "recommended": 0,
}

In this Game example, there are three components, the game takes 70% of the non-token amount, (30% platform cut) but sets the purchase price at 15 dollars hence "minimum" is 1200 (plus the three token amounts becomes 1500). Because this is a pay-for game, and the publisher is not also doing free software, they set the recommended at zero, so the user will be presented with a dialog offering the total of 15 dollars.

If the publisher were also doing free software, they may choose to have a recommended donation value above the purchase price in which case that would be in the JSON returned.

GET /vending/{appid}/{currency}/{amount}

This endpoint simply gives the split for the given app as the POST endpoint below would. If there is an error it'll be returned in the usual way, successful currency splits would be given as:

{
    "status": "ok",
    "currency": "usd",
    "split": {
        "org.gimp.GIMP": 400,
        "org.gnome.Platform": 310,
        "org.freedesktop.Platform": 163,
        "org.flathub.Flathub": 127,
    }
}

It is up to the UI to order the split according to the components list and render things appropriately.

POST /vending/{appid}

This endpoint creates a transaction to buy/donate to {appid}. As before, this might be an app in the appstream, or a platform ID.

The POST body must be JSON of the form:

{
    "currency": "usd",
    "amount": 1500,
}

If, in the future, we support currency conversions, this will allow users to donate/make payment in their own currency instead of dollars, but for now we're locking everything to dollars.

If the amount is less than the app's computed minimum value, then it will fail:

{
    "status": "error",
    "error": "amount_too_small",
}

Exact error message may vary, and other errors (e.g. database etc) may result in an error too.

If the amount is good, then a transaction of the right shape (as per the algorithm described above) is constructed and then returned as:

{
    "status": "ok",
    "transaction": "txnid",
}

Questions

Should we limit which apps can set a donation amount above the purchase amount? perhaps if the project license is not free software?

@Taiko2k
Copy link

Taiko2k commented Apr 15, 2022

I assume the platform fees here are just an example. For the GIMP example the total platform fee is effectively 60%, which seems high. Apples take of 30% is already seen as high by the industry. Consider that too high of a fee may incentivise publishers to simply add dependencies to their own manifest. Perhaps the % donated to runtimes could be set by the app publisher instead.

@danielsilverstone-ct
Copy link
Author

I assume the platform fees here are just an example. For the GIMP example the total platform fee is effectively 60%, which seems high. Apples take of 30% is already seen as high by the industry. Consider that too high of a fee may incentivise publishers to simply add dependencies to their own manifest. Perhaps the % donated to runtimes could be set by the app publisher instead.

These were all just eamples for how it might go together; we're already working on a new approach by means of computing the platform fee first, and then leaving the app publisher to decide on the split between the app and the platforms. We'll still need to apply some semblance of minima to the platform fee though because we have fixed costs as well as percentage costs. E.g. any transaction which resulted in less than approx 30 cents to flathub would directly cost flathub to execute.

I'll be publishing a new approach to computing the split once we've finalised our discussions. Our aim is 10% for flathub usually.

@emanuelserpa
Copy link

emanuelserpa commented Feb 23, 2023

Yeah, I agree with @Taiko2k. It would be very predatory for apps like GIMP.

The thing is that Freedesktop and Gnome/KDE platform have a multiplier effect (the more apps use it, the more they earn), and they would benefit for people to use them as much as possible.

Maybe the floor should be Flathub+Freedesktop and the rest would be fixed by the GNOME or KDE (depending on what the app uses, and if the dev wants to share the floor or more) and the ceiling would be the 30%.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment