Documentation SolanaSpécification

Spécification Solana Pay v1

Résumé

Un protocole standard pour encoder les demandes de transaction Solana dans des URL afin de permettre les paiements et d'autres cas d'usage.

Cette norme s'inspire de BIP 21 et EIP 681.

Motivation

Un protocole d'URL standard pour demander des transferts SOL natifs, des transferts de jetons SPL et des transactions Solana permet une meilleure expérience utilisateur dans l'ensemble des applications et portefeuilles de l'écosystème Solana.

Ces URL peuvent être encodées dans des codes QR ou des étiquettes NFC, ou envoyées entre les utilisateurs et les applications pour demander un paiement et composer des transactions.

Les applications doivent s'assurer qu'une transaction a été confirmée et est valide avant de libérer les biens ou services vendus, ou d'accorder l'accès à des objets ou des événements.

Les portefeuilles mobiles doivent s'enregistrer pour gérer le schéma d'URL afin de fournir une expérience fluide et sécurisée lorsque des URL Solana Pay sont rencontrées dans l'environnement.

En standardisant une approche simple pour résoudre ces problèmes, nous garantissons une compatibilité de base entre les applications et les portefeuilles afin que les développeurs puissent se concentrer sur des abstractions de plus haut niveau.

Spécification : Demande de transfert

Une URL de demande de transfert Solana Pay décrit une demande non interactive pour un transfert de SOL ou de jeton SPL.

solana:<recipient>
?amount=<amount>
&spl-token=<spl-token>
&reference=<reference>
&label=<label>
&message=<message>
&memo=<memo>

La demande est non interactive car les paramètres de l'URL sont utilisés par un portefeuille pour composer directement une transaction.

Destinataire

Un seul champ recipient est requis comme chemin d'accès. La valeur doit être la clé publique encodée en base58 d'un compte SOL natif. Les associated token accounts ne doivent pas être utilisés.

Au lieu de cela, pour demander un transfert de jeton SPL, le champ spl-token doit être utilisé pour spécifier un mint de jeton SPL, à partir duquel l'adresse de token account associée du destinataire doit être dérivée.

Montant

Un seul champ amount est autorisé en tant que paramètre de requête optionnel. La valeur doit être un nombre entier non négatif ou un nombre décimal en unités « utilisateur ». Pour SOL, cela signifie SOL et non lamport. Pour les tokens, utilisez uiAmountString et non amount.

0 est une valeur valide. Si la valeur est un nombre décimal inférieur à 1, il doit avoir un 0 avant le .. La notation scientifique est interdite.

Si aucune valeur n'est fournie, le portefeuille doit inviter l'utilisateur à saisir le montant. Si le nombre de décimales dépasse ce qui est pris en charge pour SOL (9) ou le jeton SPL (spécifique au mint), le portefeuille doit rejeter l'URL comme mal formée.

Jeton SPL

Un seul champ spl-token est autorisé en tant que paramètre de requête optionnel. La valeur doit être la clé publique encodée en base58 d'un mint account de jeton SPL.

Si le champ est fourni, la convention Associated Token Program doit être utilisée, et le portefeuille doit inclure une instruction TokenProgram.Transfer ou TokenProgram.TransferChecked comme dernière instruction de la transaction.

Si le champ n'est pas fourni, l'URL décrit un transfert SOL natif, et le portefeuille doit inclure une instruction SystemProgram.Transfer comme dernière instruction de la transaction à la place.

Le portefeuille doit dériver l'adresse ATA à partir des champs recipient et spl-token. Les transferts vers des token account auxiliaires ne sont pas pris en charge.

Référence

Plusieurs champs reference sont autorisés en tant que paramètres de requête optionnels. Les valeurs doivent être des tableaux de 32 octets encodés en base58. Ceux-ci peuvent ou non être des clés publiques, sur ou hors de la courbe, et peuvent ou non correspondre à des comptes sur Solana.

Si les valeurs sont fournies, le portefeuille doit les inclure dans l'ordre fourni en tant que clés en lecture seule, non-signataires de l'instruction SystemProgram.Transfer ou TokenProgram.Transfer/TokenProgram.TransferChecked dans la transaction de paiement. Les valeurs peuvent être ou non uniques à la demande de paiement, et peuvent correspondre ou non à un compte sur Solana.

Étant donné que les validateurs Solana indexent les transactions par ces clés de compte, les valeurs reference peuvent être utilisées comme identifiants clients (identifiants utilisables avant de connaître la transaction de paiement finale). La méthode RPC getSignaturesForAddress peut être utilisée pour localiser les transactions de cette manière.

Libellé

Un seul champ label est autorisé en tant que paramètre de requête optionnel. La valeur doit être une chaîne UTF-8 encodée en URL qui décrit la source de la demande de transfert.

Par exemple, il peut s'agir du nom d'une marque, d'un magasin, d'une application ou d'une personne effectuant la demande. Le portefeuille doit décoder l'URL la valeur et afficher la valeur décodée à l'utilisateur.

Message

Un seul champ message est autorisé en tant que paramètre de requête optionnel. La valeur doit être une chaîne UTF-8 encodée en URL qui décrit la nature de la demande de transfert.

Par exemple, il peut s'agir du nom d'un article acheté, d'un numéro de commande ou d'une note de remerciement. Le portefeuille doit décoder l'URL la valeur et afficher la valeur décodée à l'utilisateur.

Mémo

Un seul champ memo est autorisé en tant que paramètre de requête optionnel. La valeur doit être une chaîne UTF-8 encodée en URL qui doit être incluse dans une instruction SPL Memo dans la transaction de paiement.

Le portefeuille doit décoder l'URL la valeur et devrait afficher la valeur décodée à l'utilisateur. Le mémo sera enregistré par les validateurs et ne doit pas inclure d'informations privées ou sensibles.

Si le champ est fourni, le portefeuille doit inclure une instruction MemoProgram en tant qu'avant-dernière instruction de la transaction, immédiatement avant l'instruction de transfert SOL ou de jeton SPL, afin d'éviter toute ambiguïté avec d'autres instructions dans la transaction.

Exemples

URL décrivant une demande de transfert de 1 SOL

solana:mvines9iiHiQTysrwkJjGf2gb9Ex9jXJX8ns3qwf2kN?amount=1&label=Michael&message=Thanks%20for%20all%20the%20fish&memo=OrderId12345

URL décrivant une demande de transfert de 0,01 USDC

solana:mvines9iiHiQTysrwkJjGf2gb9Ex9jXJX8ns3qwf2kN?amount=0.01&spl-token=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v

URL décrivant une demande de transfert de SOL (montant demandé à l'utilisateur)

solana:mvines9iiHiQTysrwkJjGf2gb9Ex9jXJX8ns3qwf2kN?label=Michael

Spécification : Demande de transaction

Une URL de demande de transaction Solana Pay décrit une demande interactive pour n'importe quelle transaction Solana.

solana:<link>

La demande est interactive car les paramètres de l'URL sont utilisés par un portefeuille pour effectuer une requête HTTP afin de composer une transaction.

Lien

Un seul champ link est requis en tant que chemin d'accès. La valeur doit être une URL HTTPS absolue conditionnellement encodée en URL.

Si l'URL contient des paramètres de requête, elle doit être encodée en URL. Des paramètres de requête de protocole peuvent être ajoutés à cette spécification. L'encodage en URL de la valeur évite les conflits avec les paramètres de protocole.

Si l'URL ne contient pas de paramètres de requête, elle ne doit pas être encodée en URL. Cela produit une URL plus courte et un code QR moins dense.

Dans les deux cas, le portefeuille doit décoder l'URL. Cela n'a aucun effet si la valeur n'est pas encodée en URL. Si la valeur décodée n'est pas une URL HTTPS absolue, le portefeuille doit la rejeter comme étant mal formée.

Requête GET

Le portefeuille doit effectuer une requête JSON HTTP GET à l'URL. La requête ne doit pas identifier le portefeuille ni l'utilisateur.

Le portefeuille doit effectuer la requête avec un en-tête Accept-Encoding, et l'application doit répondre avec un en-tête Content-Encoding pour la compression HTTP.

Le portefeuille doit afficher le domaine de l'URL pendant que la requête est effectuée.

Réponse GET

Le portefeuille doit gérer les erreurs client HTTP, les erreurs serveur, et les réponses de redirection. L'application doit répondre avec celles-ci, ou avec une réponse JSON HTTP OK ayant un corps de :

{ "label": "<label>", "icon": "<icon>" }

La valeur <label> doit être une chaîne UTF-8 qui décrit la source de la demande de transaction. Par exemple, il peut s'agir du nom d'une marque, d'un magasin, d'une application ou d'une personne effectuant la demande.

La valeur <icon> doit être une URL HTTP ou HTTPS absolue d'une image d'icône. Le fichier doit être une image SVG, PNG ou WebP, sinon le portefeuille doit la rejeter comme mal formée.

Le portefeuille ne doit pas mettre en cache la réponse, sauf si les en-têtes de mise en cache HTTP le spécifient.

Le portefeuille doit afficher l'étiquette et afficher l'image de l'icône à l'utilisateur.

Requête POST

Le portefeuille doit effectuer une requête JSON HTTP POST vers l'URL avec un corps de :

{ "account": "<account>" }

La valeur <account> doit être la clé publique encodée en base58 d'un compte pouvant signer la transaction.

Le portefeuille doit effectuer la requête avec un en-tête Accept-Encoding, et l'application doit répondre avec un en-tête Content-Encoding pour la compression HTTP.

Le portefeuille doit afficher le domaine de l'URL pendant que la requête est effectuée. Si une requête GET a été effectuée, le portefeuille doit également afficher l'étiquette et afficher l'image de l'icône provenant de la réponse.

Réponse POST

Le portefeuille doit gérer les erreurs client HTTP, les erreurs serveur, et les réponses de redirection. L'application doit répondre avec celles-ci, ou avec une réponse JSON HTTP OK ayant un corps de :

{ "transaction": "<transaction>" }

La valeur <transaction> doit être une transaction sérialisée encodée en base64. Le portefeuille doit décoder la transaction en base64 et la désérialiser.

L'application peut répondre avec une transaction partiellement ou entièrement signée. Le portefeuille doit valider la transaction comme non fiable.

Signatures vides

Si les signatures de la transaction sont vides :

  • L'application doit définir le feePayer sur le account dans la requête, ou la valeur zéro (new PublicKey(0) ou new PublicKey("11111111111111111111111111111111")).
  • L'application doit définir le recentBlockhash sur le blockhash le plus récent, ou la valeur zéro (new PublicKey(0).toBase58() ou "11111111111111111111111111111111").
  • Le portefeuille doit ignorer le feePayer dans la transaction et définir le feePayer sur le account dans la requête.
  • Le portefeuille doit ignorer le recentBlockhash dans la transaction et définir le recentBlockhash sur le blockhash le plus récent.

Signatures non vides

Si les signatures de la transaction ne sont pas vides :

Le portefeuille ne doit signer la transaction qu'avec le account dans la requête, et doit le faire uniquement si une signature pour le account dans la requête est attendue.

Si une signature autre qu'une signature pour le account dans la requête est attendue, le portefeuille doit rejeter la transaction comme malveillante.

Champ de message optionnel

L'application peut également inclure un champ message optionnel dans le corps de la réponse :

{ "message": "<message>", "transaction": "<transaction>" }

La valeur <message> doit être une chaîne UTF-8 qui décrit la nature de la réponse à la transaction.

Par exemple, il peut s'agir du nom d'un article acheté, d'une réduction appliquée à l'achat ou d'un message de remerciement. Le portefeuille doit afficher cette valeur à l'utilisateur.

Le portefeuille et l'application doivent accepter des champs supplémentaires dans le corps de la requête et le corps de la réponse, qui pourront être ajoutés par les spécifications futures.

Exemples

URL décrivant une demande de transaction

solana:https://example.com/solana-pay

URL décrivant une demande de transaction avec paramètres de requête

solana:https%3A%2F%2Fexample.com%2Fsolana-pay%3Forder%3D12345

Exemple de requête GET

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

Exemple de réponse GET

HTTP/1.1 200 OK
Connection: close
Content-Type: application/json
Content-Length: 62
Content-Encoding: gzip
{"label":"Michael Vines","icon":"https://example.com/icon.svg"}

Exemple de requête POST

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"}

Exemple de réponse POST

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"}

Extensions

Des formats et champs supplémentaires peuvent être intégrés à cette spécification pour permettre de nouveaux cas d'usage tout en assurant la compatibilité avec les applications et les portefeuilles.

Veuillez ouvrir une issue GitHub pour proposer des modifications à la spécification afin de solliciter les retours des développeurs d'applications et de portefeuilles.

Un exemple concret d'une telle proposition.

Voir aussi

Is this page helpful?

Géré par

© 2026 Fondation Solana.
Tous droits réservés.
Restez connecté