Skip to content

MSC4286: App store compliant handling of payment links within events #4286

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 149 additions & 0 deletions proposals/4286-external-payment-details.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
# MSC4286: App store compliant handling of payment links within events
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am unsure if we should “approve” questionable legality practices of apple in Matrix Spec. Especially considering the recently lost court cases, I believe this is the wrong move and instead, at most should be handled on an implementation basis rather than spec.

See for example, https://techcrunch.com/2025/04/30/epic-games-just-scored-a-major-win-against-apple/ as a randomly chosen source for this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Matrix is built for the real world, and the sad reality is that today in most jurisdictions Apple still has this rule in place and has had it for many years - even if there's an injunction against it at the time of writing in the US. The fact that it's legally questionable is orthogonal to the fact that exists and impacts every Matrix client on the iOS App Store, and therefore needs to be solved in a consistent cross-client manner - hence being in the spec.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am unsure if we should “approve” questionable legality practices of apple in Matrix Spec. Especially considering the recently lost court cases, I believe this is the wrong move and instead, at most should be handled on an implementation basis rather than spec.

The problem I see with treating it as an implementation issue, is that being complaint requires cooperation between the homeserver and the client.

Where it is a "closed" homeserver that has a restricted client (i.e. the server and client are under the control of the same entity), treating it as implementation is fine.

However, if a homeserver wants to operate on a more "open" basis and allow the users to pick their client, I believe it would be beneficial to have this in the specification.


## Background

Developers of Matrix clients that are distributed via the Apple [App Store](https://www.apple.com/app-store/)
must comply with the [App Review Guidelines](https://developer.apple.com/app-store/review/guidelines/).

These guidelines include restrictions on which payment methods are offered and how they are accessed.

If a homeserver operator requires a paid plan for usage of some capabilities then a conflict of
interests can arise between the homserver operator and the application developer:

The homeserver operator may wish to promote the paid service by sending
[Server Notices](https://spec.matrix.org/v1.14/client-server-api/#server-notices) to the user or
some equivalent mechanism. However, the App Review Guidelines impose restrictions on the Matrix
client such as:

> **3.1.3(f) Free Stand-alone Apps:** Free apps acting as a stand-alone companion to a paid web
> based tool (e.g. VoIP, Cloud Storage, Email Services, Web Hosting) do not need to use in-app
> purchase, provided there is no purchasing inside the app, or calls to action for purchase outside
> of the app.

A Server Notice that contains a link to an account management page that offers sign up would likely
fall within the class of *"calls to action for purchase outside of the app"*.

As things stand today, if the homeserver wishes to allow users to use whichever client they wish
then the homeserver operator cannot put payment links into the events.

This MSC proposes a way in which the homeserver and the client developer can collaborate to provide
a satisfactory user experience whilst also remaining compliant.

## Proposal

Build on the `org.matrix.custom.html` format and add a permitted attribute
`data-mx-external-payment-details` to the `<span>` tag.

This would mean that clients can choose whether to render the `<span>`.

This is similar to how [spoilers](https://spec.matrix.org/v1.14/client-server-api/#spoiler-messages) work today.

An example of full event might look as follows:

```json
{
"msgtype": "m.text",
"format": "org.matrix.custom.html",
"body": "Please use a web browser to manage your account",
"formatted_body": "Please use a web browser to manage your account<span data-mx-external-payment-details> or click <a href=\"https://account.example.com/plan\">here</a></span>"
}
```

On a client that is aware of the new attribute and chooses to not render, it would appear as:

> Please use a web browser to manage your account
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like this is a major downgrade in UX and arguably makes the app unusable. Imho I rather would see the APIs standardized to be native to the app rather than this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i suspect you are missing the point of the MSC, which is that some appstores don't let you manage purchases with native code in the app or with links to external web services.


However, on a client that is either not-aware or chooses to render, then it would appear as:

> Please use a web browser to manage your account or click [here](https://account.example.com/plan)

## Potential issues

### Opt-in for clients means potentially not compliant by default

When formulating this proposal, it was considered what would be the best approach to balance: work
effort, client compliance, and user experience.

The following table outlines the options that were considered:

Approach | Description | For | Against
-|-|-|-
Restricted clients opt-in (as proposed) | Additional metadata is added to events so that"restricted" clients can hide the restricted content | - Only restricted clients (e.g. iOS) need to be updated<br>- No change needed for other clients | - Restricted clients may not be compliant by default, as they need to be aware of and implement this new capability
Compliant by default | Remove payment links from events. Then provide metadata to say that there is version of the content with links | - All clients are compliant by default as no payment links are present| - All non-restricted clients would ideally be updated to be aware of the presence of the extended content and deliver the best UX

The first approach was chosen as it is the least disruptive to existing clients and that there is a
small number of known iOS clients to consider.

### Per territory restrictions

Some App Store restrictions apply per territory. In such cases a client may base the decision
whether to render on both the presence of the `data-mx-external-payment-details` attribute and the
territory that the device is configured against.

## Alternatives

### Additional field along side the `formatted_body`

Instead of modifying the `formatted_body` you could add a field alongside that the client could
choose to render.

e.g.

```json
{
"msgtype": "m.text",
"format": "org.matrix.custom.html",
"body": "Please use a web browser to manage your account",
"formatted_body": "Please use a web browser to manage your account",
"formatted_body_with_external_payment_details": "You can manage your account <a href=\"https://account.example.com/plan\">here</a>"
}
```

Or the inverse:

```json
{
"msgtype": "m.text",
"format": "org.matrix.custom.html",
"body": "Please use a web browser to manage your account",
"formatted_body": "You can manage your account <a href=\"https://account.example.com/plan\">here</a>",
"formatted_body_without_external_payment_details": "Please use a web browser to manage your account"
}
```

A benefit of this approach is that the entire rendering can be replaced and not just that which is
covered by the `span`.

### Extensible events

One could define a mix-in called `m.payment_details_hidden` with content which should be displayed
by clients which know they should hide payment details. Something like this:

```json
{
"msgtype": "m.text",
"format": "org.matrix.custom.html",
"body": "Please use a web browser to manage your account",
"formatted_body": "You can manage your account <a href=\"https://account.example.com/plan\">here</a>",
"m.payment_details_hidden": {
"m.text": "Please use a web browser to manage your account",
"m.html": "Please use a web browser to manage your account",
}
}
```

However, this is clunky and has more duplication, and the spoiler-text inspired approach seems good enough.

## Security considerations

No additional requirements. The client should continue apply the existing security constraints to `formatted_body`.

## Unstable prefix

The following should be used:

- `data-msc4286-external-payment-details` instead of `data-mx-external-payment-details`

## Dependencies

None.