Περίληψη
Ένα τυποποιημένο πρωτόκολλο για την κωδικοποίηση αιτημάτων συναλλαγών Solana σε URLs, ώστε να καθίσταται δυνατή η πραγματοποίηση πληρωμών και άλλες περιπτώσεις χρήσης.
Έχει επιτευχθεί γενική συναίνεση σχετικά με αυτήν την προδιαγραφή και υπάρχουν υλοποιήσεις στα Phantom, FTX και Slope.
Αυτό το πρότυπο αντλεί έμπνευση από το BIP 21 και το EIP 681.
Αιτιολογία
Ένα τυποποιημένο πρωτόκολλο URL για την αίτηση εγγενών μεταφορών SOL, μεταφορών SPL Token και συναλλαγών Solana επιτρέπει μια καλύτερη εμπειρία χρήστη σε εφαρμογές και πορτοφόλια στο οικοσύστημα Solana.
Αυτά τα URLs μπορούν να κωδικοποιηθούν σε QR codes ή NFC tags, ή να αποσταλούν μεταξύ χρηστών και εφαρμογών για την αίτηση πληρωμής και τη σύνθεση συναλλαγών.
Οι εφαρμογές θα πρέπει να διασφαλίζουν ότι μια συναλλαγή έχει επιβεβαιωθεί και είναι έγκυρη πριν παραδώσουν προϊόντα ή υπηρεσίες που πωλούνται, ή παραχωρήσουν πρόσβαση σε αντικείμενα ή εκδηλώσεις.
Τα πορτοφόλια για κινητά θα πρέπει να εγγραφούν για το χειρισμό του σχήματος URL ώστε να παρέχουν μια απρόσκοπτη αλλά ασφαλή εμπειρία όταν εντοπίζονται URLs Solana Pay στο περιβάλλον.
Τυποποιώντας μια απλή προσέγγιση για την επίλυση αυτών των προβλημάτων, διασφαλίζουμε τη βασική συμβατότητα των εφαρμογών και των πορτοφολιών, ώστε οι προγραμματιστές να μπορούν να επικεντρωθούν σε αφαιρέσεις υψηλότερου επιπέδου.
Προδιαγραφή: Αίτημα Μεταφοράς
Ένα URL αιτήματος μεταφοράς Solana Pay περιγράφει ένα μη διαδραστικό αίτημα για μεταφορά SOL ή SPL Token.
solana:<recipient>?amount=<amount>&spl-token=<spl-token>&reference=<reference>&label=<label>&message=<message>&memo=<memo>
Το αίτημα είναι μη διαδραστικό επειδή οι παράμετροι στο URL χρησιμοποιούνται από ένα πορτοφόλι για να συνθέσει απευθείας μια συναλλαγή.
Παραλήπτης
Ένα μοναδικό πεδίο recipient απαιτείται ως το pathname. Η τιμή πρέπει να είναι
το δημόσιο κλειδί κωδικοποιημένο σε base58 ενός εγγενούς λογαριασμού SOL. Δεν
πρέπει να χρησιμοποιούνται associated token accounts.
Αντίθετα, για να ζητήσετε μεταφορά SPL Token, το πεδίο spl-token πρέπει να
χρησιμοποιηθεί για να καθορίσετε ένα SPL Token mint, από το οποίο πρέπει να
παραχθεί η διεύθυνση του associated token account του παραλήπτη.
Ποσό
Ένα μόνο πεδίο amount επιτρέπεται ως προαιρετική παράμετρος ερωτήματος. Η τιμή
πρέπει να είναι ένας μη αρνητικός ακέραιος ή δεκαδικός αριθμός "μονάδων χρήστη".
Για SOL, αυτό σημαίνει SOL και όχι lamport. Για tokens, χρησιμοποιήστε
uiAmountString και όχι amount.
Το 0 είναι έγκυρη τιμή. Εάν η τιμή είναι δεκαδικός αριθμός μικρότερος από 1,
πρέπει να έχει ένα προπορευόμενο 0 πριν την .. Η επιστημονική σημειογραφία
απαγορεύεται.
Εάν δεν παρέχεται τιμή, το πορτοφόλι πρέπει να ζητήσει από τον χρήστη το ποσό. Εάν ο αριθμός των δεκαδικών ψηφίων υπερβαίνει αυτό που υποστηρίζεται για το SOL (9) ή το SPL Token (ανάλογα με το mint), το πορτοφόλι πρέπει να απορρίψει το URL ως κακοσχηματισμένο.
SPL Token
Ένα μόνο πεδίο spl-token επιτρέπεται ως προαιρετική παράμετρος ερωτήματος. Η
τιμή πρέπει να είναι το κωδικοποιημένο σε base58 δημόσιο κλειδί ενός λογαριασμού
SPL Token mint.
Εάν παρέχεται το πεδίο, η σύμβαση
Associated Token Account
πρέπει να χρησιμοποιηθεί, και το πορτοφόλι πρέπει να συμπεριλάβει μια εντολή
TokenProgram.Transfer ή TokenProgram.TransferChecked ως την τελευταία εντολή
της συναλλαγής.
Εάν το πεδίο δεν παρέχεται, το URL περιγράφει μια εγγενή μεταφορά SOL, και το
πορτοφόλι πρέπει να συμπεριλάβει μια εντολή SystemProgram.Transfer ως την
τελευταία εντολή της συναλλαγής.
Το πορτοφόλι πρέπει να παράγει τη διεύθυνση ATA από τα πεδία recipient και
spl-token. Οι μεταφορές σε βοηθητικούς λογαριασμούς token δεν υποστηρίζονται.
Αναφορά
Πολλαπλά πεδία reference επιτρέπονται ως προαιρετικές παράμετροι ερωτήματος.
Οι τιμές πρέπει να είναι κωδικοποιημένοι σε base58 πίνακες 32 bytes. Αυτά μπορεί
ή όχι να είναι δημόσια κλειδιά, επί ή εκτός της καμπύλης, και μπορεί ή όχι να
αντιστοιχούν σε λογαριασμούς στο Solana.
Εάν παρέχονται οι τιμές, το πορτοφόλι πρέπει να τις συμπεριλάβει με τη σειρά που
παρέχονται ως κλειδιά μόνο για ανάγνωση, χωρίς υπογραφή στην εντολή
SystemProgram.Transfer ή
TokenProgram.Transfer/TokenProgram.TransferChecked στη συναλλαγή πληρωμής.
Οι τιμές μπορεί να είναι ή να μην είναι μοναδικές για το αίτημα πληρωμής, και
μπορεί να αντιστοιχούν ή όχι σε έναν λογαριασμό στο Solana.
Επειδή οι επικυρωτές του Solana δημιουργούν ευρετήριο συναλλαγών βάσει αυτών των
κλειδιών λογαριασμού, οι τιμές reference μπορούν να χρησιμοποιηθούν ως
αναγνωριστικά πελάτη (αναγνωριστικά που μπορούν να χρησιμοποιηθούν πριν
γνωρίζουμε την τελική συναλλαγή πληρωμής). Η μέθοδος RPC
getSignaturesForAddress
μπορεί να χρησιμοποιηθεί για τον εντοπισμό συναλλαγών με αυτόν τον τρόπο.
Ετικέτα
Επιτρέπεται μία μόνο παράμετρος ερωτήματος label ως προαιρετική. Η τιμή πρέπει
να είναι ένα
κωδικοποιημένο URL
αλφαριθμητικό UTF-8 που περιγράφει την πηγή του αιτήματος μεταφοράς.
Για παράδειγμα, αυτό μπορεί να είναι το όνομα μιας επωνυμίας, καταστήματος, εφαρμογής ή προσώπου που υποβάλλει το αίτημα. Το πορτοφόλι θα πρέπει να αποκωδικοποιήσει το URL της τιμής και να εμφανίσει την αποκωδικοποιημένη τιμή στον χρήστη.
Μήνυμα
Επιτρέπεται μία μόνο παράμετρος ερωτήματος message ως προαιρετική. Η τιμή
πρέπει να είναι ένα
κωδικοποιημένο URL
αλφαριθμητικό UTF-8 που περιγράφει τη φύση του αιτήματος μεταφοράς.
Για παράδειγμα, αυτό μπορεί να είναι το όνομα ενός αντικειμένου που αγοράζεται, ένα αναγνωριστικό παραγγελίας ή ένα σημείωμα ευχαριστίας. Το πορτοφόλι θα πρέπει να αποκωδικοποιήσει το URL της τιμής και να εμφανίσει την αποκωδικοποιημένη τιμή στον χρήστη.
Υπόμνημα
Επιτρέπεται μία μόνο παράμετρος ερωτήματος memo ως προαιρετική. Η τιμή πρέπει
να είναι ένα
κωδικοποιημένο URL
αλφαριθμητικό UTF-8 που πρέπει να συμπεριληφθεί σε μια εντολή
SPL Memo στη συναλλαγή πληρωμής.
Το πορτοφόλι πρέπει να αποκωδικοποιήσει το URL της τιμής και θα πρέπει να εμφανίσει την αποκωδικοποιημένη τιμή στον χρήστη. Το υπόμνημα θα καταγραφεί από τους επικυρωτές και δεν πρέπει να περιλαμβάνει ιδιωτικές ή ευαίσθητες πληροφορίες.
Εάν παρέχεται το πεδίο, το πορτοφόλι πρέπει να περιλαμβάνει μια εντολή
MemoProgram ως την προτελευταία εντολή της συναλλαγής, αμέσως πριν από την
εντολή μεταφοράς SOL ή SPL Token, για να αποφευχθεί η ασάφεια με άλλες εντολές
στη συναλλαγή.
Παραδείγματα
URL που περιγράφει ένα αίτημα μεταφοράς για 1 SOL
solana:mvines9iiHiQTysrwkJjGf2gb9Ex9jXJX8ns3qwf2kN?amount=1&label=Michael&message=Thanks%20for%20all%20the%20fish&memo=OrderId12345
URL που περιγράφει ένα αίτημα μεταφοράς για 0.01 USDC
solana:mvines9iiHiQTysrwkJjGf2gb9Ex9jXJX8ns3qwf2kN?amount=0.01&spl-token=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
URL που περιγράφει ένα αίτημα μεταφοράς για SOL (ο χρήστης ερωτάται για το ποσό)
solana:mvines9iiHiQTysrwkJjGf2gb9Ex9jXJX8ns3qwf2kN?label=Michael
Προδιαγραφή: Αίτημα Συναλλαγής
Ένα URL αιτήματος συναλλαγής Solana Pay περιγράφει ένα διαδραστικό αίτημα για οποιαδήποτε συναλλαγή Solana.
solana:<link>
Το αίτημα είναι διαδραστικό επειδή οι παράμετροι στο URL χρησιμοποιούνται από ένα πορτοφόλι για να πραγματοποιήσει ένα αίτημα HTTP για τη σύνθεση μιας συναλλαγής.
Σύνδεσμος
Απαιτείται ένα μόνο πεδίο link ως το όνομα διαδρομής. Η τιμή πρέπει να είναι
μια υπό συνθήκη
κωδικοποιημένη σε URL
απόλυτη διεύθυνση HTTPS URL.
Εάν το URL περιέχει παραμέτρους ερωτήματος, πρέπει να κωδικοποιηθεί σε URL. Οι παράμετροι ερωτήματος πρωτοκόλλου μπορεί να προστεθούν σε αυτήν την προδιαγραφή. Η κωδικοποίηση σε URL της τιμής αποτρέπει τη σύγκρουση με τις παραμέτρους πρωτοκόλλου.
Εάν το URL δεν περιέχει παραμέτρους ερωτήματος, δεν θα πρέπει να κωδικοποιηθεί σε URL. Αυτό παράγει ένα συντομότερο URL και έναν λιγότερο πυκνό κωδικό QR.
Σε κάθε περίπτωση, το πορτοφόλι πρέπει να αποκωδικοποιήσει το URL την τιμή. Αυτό δεν έχει επίδραση εάν η τιμή δεν είναι κωδικοποιημένη σε URL. Εάν η αποκωδικοποιημένη τιμή δεν είναι απόλυτη διεύθυνση HTTPS URL, το πορτοφόλι πρέπει να την απορρίψει ως εσφαλμένη.
Αίτημα GET
Το πορτοφόλι θα πρέπει να πραγματοποιήσει ένα HTTP αίτημα GET JSON στο URL. Το
αίτημα δεν θα πρέπει να αναγνωρίζει το πορτοφόλι ή τον χρήστη.
Το πορτοφόλι θα πρέπει να πραγματοποιήσει το αίτημα με μια κεφαλίδα Accept-Encoding, και η εφαρμογή θα πρέπει να απαντήσει με μια κεφαλίδα Content-Encoding για συμπίεση HTTP.
Το πορτοφόλι θα πρέπει να εμφανίζει το domain του URL καθώς πραγματοποιείται το αίτημα.
Απόκριση GET
Το πορτοφόλι πρέπει να χειρίζεται τις HTTP
αποκρίσεις σφάλματος πελάτη,
αποκρίσεις σφάλματος διακομιστή,
και
αποκρίσεις ανακατεύθυνσης.
Η εφαρμογή πρέπει να απαντά με αυτές, ή με μια HTTP OK JSON απόκριση με σώμα:
{ "label": "<label>", "icon": "<icon>" }
Η τιμή <label> πρέπει να είναι μια συμβολοσειρά UTF-8 που περιγράφει την πηγή
του αιτήματος συναλλαγής. Για παράδειγμα, αυτό μπορεί να είναι το όνομα μιας
επωνυμίας, καταστήματος, εφαρμογής ή προσώπου που πραγματοποιεί το αίτημα.
Η τιμή <icon> πρέπει να είναι ένα απόλυτο HTTP ή HTTPS URL μιας εικόνας
εικονιδίου. Το αρχείο πρέπει να είναι εικόνα SVG, PNG ή WebP, διαφορετικά το
πορτοφόλι πρέπει να την απορρίψει ως ελαττωματική.
Το πορτοφόλι δεν θα πρέπει να αποθηκεύει την απόκριση σε cache εκτός εάν υποδεικνύεται από κεφαλίδες απόκρισης HTTP caching.
Το πορτοφόλι θα πρέπει να εμφανίζει την ετικέτα και να αποδίδει την εικόνα του εικονιδίου στον χρήστη.
Αίτημα POST
Το πορτοφόλι πρέπει να πραγματοποιεί ένα HTTP POST JSON αίτημα στο URL με
σώμα:
{ "account": "<account>" }
Η τιμή <account> πρέπει να είναι το δημόσιο κλειδί σε κωδικοποίηση base58 ενός
λογαριασμού που μπορεί να υπογράψει τη συναλλαγή.
Το πορτοφόλι θα πρέπει να πραγματοποιεί το αίτημα με μια κεφαλίδα Accept-Encoding, και η εφαρμογή θα πρέπει να απαντά με μια κεφαλίδα Content-Encoding για συμπίεση HTTP.
Το πορτοφόλι θα πρέπει να εμφανίζει το domain του URL καθώς πραγματοποιείται το
αίτημα. Εάν είχε πραγματοποιηθεί αίτημα GET, το πορτοφόλι θα πρέπει επίσης να
εμφανίζει την ετικέτα και να αποδίδει την εικόνα του εικονιδίου από την
απόκριση.
Απόκριση POST
Το πορτοφόλι πρέπει να χειρίζεται τις HTTP
αποκρίσεις σφάλματος πελάτη,
αποκρίσεις σφάλματος διακομιστή,
και
αποκρίσεις ανακατεύθυνσης.
Η εφαρμογή πρέπει να απαντά με αυτές, ή με μια HTTP OK JSON απόκριση με σώμα:
{ "transaction": "<transaction>" }
Η τιμή <transaction> πρέπει να είναι μια κωδικοποιημένη σε base64
σειριοποιημένη συναλλαγή.
Το πορτοφόλι πρέπει να αποκωδικοποιήσει τη συναλλαγή από base64 και να την
αποσειριοποιήσει.
Η εφαρμογή μπορεί να απαντήσει με μια μερικώς ή πλήρως υπογεγραμμένη συναλλαγή. Το πορτοφόλι πρέπει να επικυρώσει τη συναλλαγή ως μη αξιόπιστη.
Κενές Υπογραφές
Εάν οι
signatures
της συναλλαγής είναι κενές:
- Η εφαρμογή θα πρέπει να ορίσει το
feePayerστοaccountτου αιτήματος, ή στη μηδενική τιμή (new PublicKey(0)ήnew PublicKey("11111111111111111111111111111111")). - Η εφαρμογή θα πρέπει να ορίσει το
recentBlockhashστο πιο πρόσφατο blockhash, ή στη μηδενική τιμή (new PublicKey(0).toBase58()ή"11111111111111111111111111111111"). - Το πορτοφόλι πρέπει να αγνοήσει το
feePayerστη συναλλαγή και να ορίσει τοfeePayerστοaccountτου αιτήματος. - Το πορτοφόλι πρέπει να αγνοήσει το
recentBlockhashστη συναλλαγή και να ορίσει τοrecentBlockhashστο πιο πρόσφατο blockhash.
Εάν οι
signatures
της συναλλαγής δεν είναι κενές:
- Η εφαρμογή πρέπει να ορίσει το
feePayerστο δημόσιο κλειδί της πρώτης υπογραφής. - Η εφαρμογή πρέπει να ορίσει το
recentBlockhashστο πιο πρόσφατο blockhash. - Η εφαρμογή πρέπει να σειριοποιήσει και να αποσειριοποιήσει τη συναλλαγή πριν την υπογράψει. Αυτό διασφαλίζει τη συνεπή σειρά των κλειδιών λογαριασμών, ως λύση για αυτό το ζήτημα.
- Το πορτοφόλι δεν πρέπει να ορίσει το
feePayerκαι τοrecentBlockhash. - Το πορτοφόλι πρέπει να επαληθεύσει τις υπογραφές, και εάν κάποια είναι μη έγκυρη, το πορτοφόλι πρέπει να απορρίψει τη συναλλαγή ως εσφαλμένη.
Το πορτοφόλι πρέπει να υπογράψει τη συναλλαγή μόνο με το account του
αιτήματος, και πρέπει να το κάνει μόνο εάν αναμένεται υπογραφή για το account
του αιτήματος.
Εάν αναμένεται οποιαδήποτε υπογραφή εκτός από υπογραφή για το account του
αιτήματος, το πορτοφόλι πρέπει να απορρίψει τη συναλλαγή ως κακόβουλη.
Η εφαρμογή μπορεί επίσης να περιλαμβάνει ένα προαιρετικό πεδίο message στο
σώμα της απόκρισης:
{ "message": "<message>", "transaction": "<transaction>" }
Η τιμή <message> πρέπει να είναι μια συμβολοσειρά UTF-8 που περιγράφει τη φύση
της απόκρισης της συναλλαγής.
Για παράδειγμα, αυτό μπορεί να είναι το όνομα ενός προϊόντος που αγοράζεται, μια έκπτωση που εφαρμόζεται στην αγορά ή ένα σημείωμα ευχαριστίας. Το πορτοφόλι θα πρέπει να εμφανίσει την τιμή στον χρήστη.
Το πορτοφόλι και η εφαρμογή θα πρέπει να επιτρέπουν πρόσθετα πεδία στο σώμα του αιτήματος και στο σώμα της απόκρισης, τα οποία μπορεί να προστεθούν από μελλοντικές προδιαγραφές.
Παράδειγμα
URL που περιγράφει ένα αίτημα συναλλαγής.
solana:https://example.com/solana-pay
URL που περιγράφει ένα αίτημα συναλλαγής με παραμέτρους ερωτήματος.
solana:https%3A%2F%2Fexample.com%2Fsolana-pay%3Forder%3D12345
Αίτημα GET
GET /solana-pay?order=12345 HTTP/1.1Host: example.comConnection: closeAccept: application/jsonAccept-Encoding: br, gzip, deflate
Απόκριση GET
HTTP/1.1 200 OKConnection: closeContent-Type: application/jsonContent-Length: 62Content-Encoding: gzip{"label":"Michael Vines","icon":"https://example.com/icon.svg"}
Αίτημα POST
POST /solana-pay?order=12345 HTTP/1.1Host: example.comConnection: closeAccept: application/jsonAccept-Encoding: br, gzip, deflateContent-Type: application/jsonContent-Length: 57{"account":"mvines9iiHiQTysrwkJjGf2gb9Ex9jXJX8ns3qwf2kN"}
Απόκριση POST
HTTP/1.1 200 OKConnection: closeContent-Type: application/jsonContent-Length: 298Content-Encoding: gzip{"message":"Thanks for all the fish","transaction":"AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAECC4JMKqNplIXybGb/GhK1ofdVWeuEjXnQor7gi0Y2hMcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQECAAAMAgAAAAAAAAAAAAAA"}
Επεκτάσεις
Πρόσθετες μορφές και πεδία μπορεί να ενσωματωθούν σε αυτήν την προδιαγραφή για να ενεργοποιήσουν νέες περιπτώσεις χρήσης διασφαλίζοντας παράλληλα τη συμβατότητα με εφαρμογές και πορτοφόλια.
Παρακαλούμε ανοίξτε ένα ζήτημα στο Github για να προτείνετε αλλαγές στην προδιαγραφή προκειμένου να λάβετε ανατροφοδότηση από προγραμματιστές εφαρμογών και πορτοφολιών.
Is this page helpful?