Skip to content
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

[feature request] Support for redirect urls on txn success #162

Open
uveerma opened this issue Sep 25, 2022 · 17 comments · Fixed by #193
Open

[feature request] Support for redirect urls on txn success #162

uveerma opened this issue Sep 25, 2022 · 17 comments · Fixed by #193

Comments

@uveerma
Copy link

uveerma commented Sep 25, 2022

Wallet providers who support Solana Pay can allow user redirecting on successful txns, just like deep link implementations. This UX will make it familiar with GPay, Apple Pay, etc, and can provide merchants or payment providers a room to support new features.

Motivation: - Recently at Delhi HH it was my first experience seeing people actually mint NFTs IRL by scanning a Solana pay QR Code. While the experience was seamless, post minting, new users who created their first ever wallet had no idea what to further do. Wallet providers take some time to notify users that they got an NFT and further loading of this NFT image takes a lot of time, meanwhile the end user remains clueless.

  • Similarly, there can be a case where a user paid to buy something with Solana pay on web apps (not taking pos reference here) but is confused if their user was actually marked paid or not.
  • Businesses accepting Solana pay will expect their user to come back to their site and keep surfing more post a successful order, instead of remaining on the wallet provider apps, without a specific action on what to do next
  • NFT and Payment Providers would like to show users with actual NFT minted and/or the invoice of payment made respectively as soon as the txn is confirmed, instead of having them wait and figure things out themselves

Implementation: - There's little to no change on the protocol level for this feature, and most work will be on the wallet provider side. As a user, we can pass a redirect_url param in the POST response along with other parameters like message, which can give us an extra space to make Redirect URLs more targeted, e.g. passing mint address for newly minted NFT with an explorer link by which user can see metadata for their minted NFTs instantly.

Excited to hear everyone's feedback and views on this addition. Thanks to @cogoo & @mcintyre94 for listening to this idea initially, and providing their input!

@jordaaash
Copy link
Collaborator

I've heard a few requests along these lines but they haven't gotten into implementation details.

Can you provide a proposal here of how you see the spec changing, how this would work with transfer requests, how this would work with transaction requests, how it work in an app-to-app context on the same mobile device, security implications, and a list of use cases for this?

@peerwaya
Copy link

peerwaya commented Sep 25, 2022

This makes sense. Our usecase is using solana pay transaction request to make in app payments. Once the user opens solana pay and the transaction is successful, the user stays on the wallet app. No way to redirect back to the app as seen in the video below.

We can enhance the response of the post request to return a success_url and cancel_url. I believe this is sufficient for wallet providers to redirect based on the outcome of the transaction. If the transaction is successful and a success_url is provided, redirect the user to the success_url. if a transaction is canceled, redirect the user to the cancel_url.

So as @Vampo7152 mentioned, nothing really changes on the protocol but its up to wallet providers to recognize success_url and passing the tx as a query parameter.

POST Request
POST /solana-pay?order=12345 HTTP/1.1
Host: example.com
Connection: close
Accept: application/json
Accept-Encoding: br, gzip, deflate
Content-Type: application/json
Content-Length: 57

{"account":"mvines9iiHiQTysrwkJjGf2gb9Ex9jXJX8ns3qwf2kN"}
POST Response
HTTP/1.1 200 OK
Connection: close
Content-Type: application/json
Content-Length: 298
Content-Encoding: gzip

{"message":"Thanks for all the fish","transaction":"AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAECC4JMKqNplIXybGb/GhK1ofdVWeuEjXnQor7gi0Y2hMcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQECAAAMAgAAAAAAAAAAAAAA", "success_url": "https://example.com/thank_you", "cancel_url": "https://example.com/cancel"}

When a transaction is successful or failed redirect the user to success_url and append the tx as a query parameter eg

https://example.com/thank_you?tx=XXXXX

When a transaction is canceled, redirect the user to the cancel_url

https://example.com/cancel

Where success_url is a valid URI. Wallets must check if they can open such url before attempting to open url
Where cancel_url is a valid URI. Wallets must check if they can open such url before attempting to open url

Use Case:
Using solana pay transaction request to make payment. There is no seamless way to redirect back to the app without the user manually visiting the calling app.

Untitled.mp4

Success flow
Notice the way the app is redirected when the user taps on done on the Binance app using Binance Pay

Untitled.1.mp4

Cancel flow

Untitled.2.mp4

@uveerma
Copy link
Author

uveerma commented Sep 26, 2022

Specification Changes:
The success and cancel redirect URLs should be limited for Transaction Requests and don't hold much usage for Transfer Requests. As @peerwaya shared above, the POST response will change this way,

HTTP/1.1 200 OK
Connection: close
Content-Type: application/json
Content-Length: 298
Content-Encoding: gzip

{"message":"Thanks for all the fish","transaction":"AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAECC4JMKqNplIXybGb/GhK1ofdVWeuEjXnQor7gi0Y2hMcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQECAAAMAgAAAAAAAAAAAAAA", "success_url": "https://example.com/thank_you", "cancel_url": "https://example.com/cancel"}

with success_url and cancel_url as two new optional JSON fields. Wallets should verify the validity of these URLs beforehand and should not support HTTP protocol URLs.

Workflow:
As shown in the above reference video of Binanace Pay and how deeplinks of wallet providers for transaction signing works, redirect URls will work in a similar manner for all modes of usage including app to app and website to app -- where mode of payment is online with a single mobile device in action.
For POS solutions, there's no direct usage for either of the redirect URLs, but providers may redirect users to a website for downloading e-receipts on each successful purchase

Usecases:

  • Payment Providers:

    • Recently we have been working on a Checkout SDK which takes in success and cancel redirect URLs from merchants while creating sessions, the ux is seamless for desktop transactions done with wallet extensions but for mobile, user remains stagnant on the wallet apps once payment is success/cancelled meanwhile the checkout site redirects them back to the provided URLs directly in the browser, so its a broken UX currently but can become flawless with this feature addition.
    • POS providers can redirect users to download e-receipts for their purchases or to some website where they can claim special offers based on their purchase metrics e.g. purchase above $500 and claim special coupon/NFT
  • Businesses:

    • Businesses using payment providers or having their in-house solution, would prefer user get redirected back to their website/app post purchase/payment to surf more products and keep user engaged without this broken link post payment. SASS services would like to have user start using services instantly after purchase and so on
  • Users:

    • success and cancel redirect URLs are important in terms of user experience to make it familiar with other web2 solutions like GPay, Apple Pay, etc. to provide a seamless user flow
    • Whether it's NFT minting, Payments, Token swap or any usecase we are missing here, instant result showcase based on the action taken by user will make them feel engaged and rest assured

@jordaaash
Copy link
Collaborator

Hmm, I don't think we want/need two fields for this. As length of the string increases, so does QR code density and likelihood of scanning problems. A single field (e.g. redirect-url, but I'm not attached to the name yet) with a URL-encoded URL value should be sufficient. That URL could be parameterized with a transaction signature on success, or an error field (#150). Cancellation could be represented either by a specific error, or by a lack of a transaction signature or an error.

redirect URLs should be limited for Transaction Requests and don't hold much usage for Transfer Requests

@Vampo7152 can you explain why this is the case?

In the above examples, I've only seen https URLs used. However, several of the use cases you're describing sounds like it requires potentially using deep links, universal links, or app links. Is this accurate? And what implications does that have?

@jordaaash
Copy link
Collaborator

@peerwaya

Wallets must check if they can open such url before attempting to open url

What do you mean by this?

For POS solutions, there's no direct usage for either of the redirect URLs, but providers may redirect users to a website for downloading e-receipts on each successful purchase

In general, this feature seems highly likely to be used for all kinds of marketing purposes and information collection (e.g. tying emails to wallet addresses). I'll solicit feedback from wallets on this once we have a practical proposal.

When combined with opening a URL on cancellation, it's also hairy -- someone scans a QR code, it makes a request, they decline to sign, then it automatically opens a URL on their phone (with their wallet address known). I can see wallets having issues with this.

@peerwaya
Copy link

peerwaya commented Sep 26, 2022

on native android and ios apps, an API exist to check if a url can be opened by an existing app eg chrome browser, safari or app. On flutter and react native it's as simple as calling canOpenUrl(url) to check if the url can be opened by an app on the device. if such exists, then the next step will be to call launchUrl(url). This is to ensure there exist an app that can handle such url. Custom url schemes used for deeplinking might be tricky with recent versions of Android and iOS as they require apps to query a known list of scheme that must be registered on the manifest or info.plist file. Therefore, the use of universal links or Web Links might be preferred.

cancelUrl does not have to pass any new information. Though the address was made known during the initial post request

POST /solana-pay?order=12345 HTTP/1.1
Host: example.com
Connection: close
Accept: application/json
Accept-Encoding: br, gzip, deflate
Content-Type: application/json
Content-Length: 57

{"account":"mvines9iiHiQTysrwkJjGf2gb9Ex9jXJX8ns3qwf2kN"}

and the server responded with

{"message":"Thanks for all the fish","transaction":"AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAECC4JMKqNplIXybGb/GhK1ofdVWeuEjXnQor7gi0Y2hMcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQECAAAMAgAAAAAAAAAAAAAA", "success_url": "https://example.com/thank_you", "cancel_url": "https://example.com/cancel"}```

@uveerma
Copy link
Author

uveerma commented Sep 26, 2022

redirect URLs should be limited for Transaction Requests and don't hold much usage for Transfer Requests

The initial idea for redirect URLs was to be unique based on the params generated in the POST response for Transaction requests, it can be added to Transfer requests as well but then the URL will be static and can get too dense if the use case is SPL Token Transfer with a memo added along.

When combined with opening a URL on cancellation, it's also hairy -- someone scans a QR code, it makes a request, they decline to sign, then it automatically opens a URL on their phone (with their wallet address known). I can see wallets having issues with this.

Yeah, cancel URLs don't help much as all transactions which can't be performed by the public key get disabled by the wallet provider already, and canceling the transaction becomes a voluntary action so we can disregard it considering security concerns based on the example you shared above.

@jordaaash
Copy link
Collaborator

Transfer requests [...] then the URL will be static

Not sure I follow this. While transfer requests can use static URLs, they are generally designed to use unique URLs because at least one reference key is expected to be unique per transaction, and reference keys can be used for all kinds of things (for example, passing encrypted data around).

If a transfer request can encode a redirect URL field and then will receive a transaction signature as a parameter to it, then the value of the field can be static (e.g. https://example.com/payment?transaction=xxx) or unique (e.g. https://example.com/payment/<reference>?transaction=xxx).

canceling the transaction [...] we can disregard it

I think it's worth fully detailing what pros and cons they might have before we throw the idea out!

@peerwaya
Copy link

peerwaya commented Sep 27, 2022

cancel_url might not be needed if we stick to only redirect_url and append an error_code param. This way, the error_code param can also be used to indicate a specific error eg user_canceled.

https://example.com/payment?error_code=user_canceled

error can be a an enum with the following values:

user_canceled,
transaction_failed,
metadata_error
unknown,

error_message can optionally be appended to describe the error in more details.

@jordaaash
Copy link
Collaborator

Let's define errors consistent with #150 (tbd)

But I don't think an error is strictly needed on cancellation since callback with no transaction signature and no error implies the request was declined. Wallet errors can be reserved for connection failures and transaction failures.

@samheutmaker
Copy link
Contributor

I'm glad to see this being discussed. A few thoughts:

When combined with opening a URL on cancellation, it's also hairy -- someone scans a QR code, it makes a request, they decline to sign, then it automatically opens a URL on their phone (with their wallet address known). I can see wallets having issues with this.

I think wallets will be concerned if the spec requires that they automatically redirect users to a URL post-transaction, whether it fails or succeeds. Instead of this, it may be better to make following redirect urls optional even if they are specified in the tx response. This allows wallets to decide if they want to auto-follow links, prompt the user to follow the link, or disallow following links. Going this route also means that these changes can be incrementally adopted by wallets while still remaining compliant with the spec. This also disallows dapp developers from be able to rely on the redirectUrl being visited after a completed transaction, which may not be ideal.

Hmm, I don't think we want/need two fields for this.

I agree that redirectUrl (or something similar) with params passed to indicate state is probably best.

Let's define errors consistent with #150 (tbd)

Definitely. This work should be completed by mid-November.

The use-cases mentioned above all seem valid. It may be worth trying to flesh out a few more of these by talking with teams that are building similar tech to make sure that we're covering our bases.

My main concern with this work is that the onus is on wallets to adopt new Solana Pay features. With #150 and #151 scheduled for release in November, and this issue stacked on top, it's beginning to look like a fairly large ask for wallets to upgrade. We should figure out how we can release all these features together so that we can go to wallets with a single request to upgrade rather than releasing changes for the first two issues and then going back one or two months laters with another ask to add support for redirectUrls.

@jordaaash
Copy link
Collaborator

We should figure out how we can release all these features together

Generally agreed, though the way I see it is --

@Jeffrieh
Copy link

Hi guys, any updates on this ?

@jordaaash
Copy link
Collaborator

@Jeffrieh please don't comment on issues like this, it notifies everyone. Instead, just subscribe to the issue.

It hasn't been determined that this is a feature that will be added to the spec, and even if it is, if it will be implemented.

@mcintyre94
Copy link
Collaborator

mcintyre94 commented Nov 28, 2022

I've been taking a closer look at the existing deeplink functionality in Phantom + Solflare, with a view to making sure that whatever we come up with is similar in functionality (wrt redirects) and provides the same security.

It is correct that all the deeplink methods take a redirect_link (it's actually required). They don't have separate success/errors, the app can distinguish them by the query params. One thing I've noticed is that the wallets display the redirect URL in full, before doing the connect/transaction/etc.

Deeplinks also return data from the wallet back to the redirect URL, encrypted with a shared secret (details here). This can be things like the connected public key, a transaction signature, and the connect token that needs to be passed into further requests. This is probably not feasible for Solana Pay:

  • We don't want apps to have to trust the wallet. In Solana Pay (unlike deeplinks), the app doesn't control or know which wallet is being used, and can't trust what it receives from the wallet.
  • For transaction requests, you'd need to have a private key of a keypair generated by the API returning the transaction, and then used to decrypt at the redirect URL. I think the data flow here is just too different to deeplinks to rely on the same sort of encryption.
  • I don't think wallets would want to be sending things like connected wallets and transaction signatures in plaintext, and we wouldn't want to encourage that

So I think the idea of having anything sent from the wallet on redirect doesn't fit well with Solana Pay.

Because of that, for transaction requests we'd like the redirect URL to be dynamic, so that the API can put the information that it needs into it. This suggests that it should be returned by the POST request (along with the transaction), since that's the only time the API knows about the user's wallet/the transaction etc. This importantly means that the wallet will be unable to display the redirect link before the user interacts, which is a different from deeplinks.

Here's roughly how I think this would work, for transfer and transaction requests

Transfer requests:

  • A new optional query parameter that takes a URL-encoded redirect URL. In practice the app would probably want this tov include an encrypted form of the reference, but that's outside the spec.
  • Before the transfer, the wallet can show this redirect link as it currently does with deeplinks
  • After the transfer, the wallet can redirect the user to that link, as-is

Transaction requests:

  • A new optional field in the POST response that takes a redirect URL. In practice the app would probably want to include an encrypted form of account keys, signatures, internal identifiers, etc.
  • Before the transaction, the wallet cannot show this redirect link because it won't know it
  • After the transaction, the wallet should show this redirect and allow the user to follow it, as-is

The redirect link can be any https: or solana: URL, including a universal link, or another Solana pay URL. Since it's defined by the POST API, there's a ton of flexibility here. We're thinking we'd block deeplinks here (except solana) for security.

Allowing a Solana Pay URL would allow chaining transactions, see https://twitter.com/jordaaash/status/1597292945090129921?s=20&t=RN7z3SwXZxcRBzzRub5E4g

But probably means that we need to have the behaviour that cancelling the request means the redirect is not followed, to avoid a malicious infinite request loop

So in summary, the main differences from deeplinks are:

  • In Solana Pay we will never send data from the wallet to the redirect URL, it's just a redirect. Everything the app needs to know will need to be in this redirect link.
  • In transaction requests the redirect link is dynamic and not known before sending the transaction

Would love to hear any thoughts on this!

@mcintyre94
Copy link
Collaborator

We're going to be looking to specify this shortly. If anyone has any feedback on the above, please let us know so that we can discuss and incorporate it!

@Majidrazaee
Copy link

عالیه

@mcintyre94 mcintyre94 reopened this Feb 28, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants