-
Notifications
You must be signed in to change notification settings - Fork 397
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
# MSC4286: App store compliant handling of payment links within events | ||
|
||
## 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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.