Περίληψη
Ένα τυποποιημένο πρωτόκολλο για την κωδικοποίηση αιτημάτων συναλλαγών Solana σε URLs ώστε να καταστούν δυνατές οι πληρωμές και άλλες περιπτώσεις χρήσης.
Αυτό το πρότυπο αντλεί έμπνευση από το BIP 21 και το EIP 681.
Αιτιολόγηση
Ένα τυποποιημένο πρωτόκολλο URL για την αίτηση εγγενών μεταφορών SOL, μεταφορών SPL Token και συναλλαγών Solana επιτρέπει μια καλύτερη εμπειρία χρήστη σε εφαρμογές και πορτοφόλια στο οικοσύστημα του Solana.
Αυτά τα URLs μπορούν να κωδικοποιηθούν σε κωδικούς QR ή ετικέτες NFC, ή να αποσταλούν μεταξύ χρηστών και εφαρμογών για την αίτηση πληρωμής και τη σύνθεση συναλλαγών.
Οι εφαρμογές πρέπει να διασφαλίζουν ότι μια συναλλαγή έχει επιβεβαιωθεί και είναι έγκυρη προτού αποδεσμεύσουν αγαθά ή υπηρεσίες που πωλούνται, ή παραχωρήσουν πρόσβαση σε αντικείμενα ή εκδηλώσεις.
Τα κινητά πορτοφόλια θα πρέπει να εγγραφούν για να χειρίζονται το σχήμα 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 και όχι lamports. Για tokens, χρησιμοποιήστε
uiAmountString και όχι amount.
Το 0 είναι έγκυρη τιμή. Εάν η τιμή είναι δεκαδικός αριθμός μικρότερος από 1,
πρέπει να έχει ένα 0 στην αρχή πριν από την .. Η επιστημονική σημειογραφία
απαγορεύεται.
Εάν δεν παρέχεται τιμή, το πορτοφόλι πρέπει να ζητήσει από τον χρήστη το ποσό. Εάν ο αριθμός των δεκαδικών ψηφίων υπερβαίνει αυτά που υποστηρίζονται για SOL (9) ή το SPL Token (ειδικό για κάθε mint account), το πορτοφόλι πρέπει να απορρίψει το URL ως εσφαλμένο.
SPL Token
Επιτρέπεται ένα μόνο πεδίο spl-token ως προαιρετική παράμετρος ερωτήματος. Η
τιμή πρέπει να είναι το κωδικοποιημένο σε base58 δημόσιο κλειδί ενός mint
account SPL Token.
Εάν παρέχεται το πεδίο, πρέπει να χρησιμοποιηθεί η σύμβαση
Associated Token Program και
το πορτοφόλι πρέπει να συμπεριλάβει μια εντολή TokenProgram.Transfer ή
TokenProgram.TransferChecked ως την τελευταία εντολή της συναλλαγής.
Εάν το πεδίο δεν παρέχεται, το URL περιγράφει μια εγγενή μεταφορά SOL και το
πορτοφόλι πρέπει να συμπεριλάβει μια εντολή SystemProgram.Transfer ως την
τελευταία εντολή της συναλλαγής.
Το πορτοφόλι πρέπει να παράγει τη διεύθυνση του associated token account από τα
πεδία recipient και spl-token. Οι μεταφορές σε βοηθητικούς token accounts
δεν υποστηρίζονται.
Αναφορά
Επιτρέπονται πολλαπλά πεδία reference ως προαιρετικές παράμετροι ερωτήματος.
Οι τιμές πρέπει να είναι κωδικοποιημένοι σε base58 πίνακες 32 bytes. Αυτοί
μπορεί ή όχι να είναι δημόσια κλειδιά, επί ή εκτός της καμπύλης, και μπορεί ή
όχι να αντιστοιχούν σε λογαριασμούς στο Solana.
Εάν παρέχονται οι τιμές, το πορτοφόλι πρέπει να τις συμπεριλάβει με τη σειρά που
παρέχονται ως κλειδιά μόνο για ανάγνωση, χωρίς υπογραφή στην εντολή
SystemProgram.Transfer ή
TokenProgram.Transfer/TokenProgram.TransferChecked στη συναλλαγή πληρωμής.
Οι τιμές μπορεί να είναι ή να μην είναι μοναδικές για το αίτημα πληρωμής, και
μπορεί να αντιστοιχούν ή όχι σε έναν λογαριασμό στο Solana.
Επειδή οι επικυρωτές του Solana δημιουργούν ευρετήρια συναλλαγών βάσει αυτών των
κλειδιών λογαριασμού, οι τιμές reference μπορούν να χρησιμοποιηθούν ως
αναγνωριστικά πελάτη (αναγνωριστικά που μπορούν να χρησιμοποιηθούν πριν γίνει
γνωστή η τελική συναλλαγή πληρωμής). Η μέθοδος RPC
getSignaturesForAddress
μπορεί να χρησιμοποιηθεί για τον εντοπισμό συναλλαγών με αυτόν τον τρόπο.
Ετικέτα
Επιτρέπεται μία μόνο παράμετρος ερωτήματος label ως προαιρετική. Η τιμή πρέπει
να είναι μια συμβολοσειρά UTF-8 με
κωδικοποίηση URL
που περιγράφει την πηγή του αιτήματος μεταφοράς.
Για παράδειγμα, αυτό μπορεί να είναι το όνομα μιας επωνυμίας, καταστήματος, εφαρμογής ή ατόμου που υποβάλλει το αίτημα. Το πορτοφόλι πρέπει να αποκωδικοποιήσει το URL της τιμής και να εμφανίσει την αποκωδικοποιημένη τιμή στον χρήστη.
Μήνυμα
Επιτρέπεται μία μόνο παράμετρος ερωτήματος message ως προαιρετική. Η τιμή
πρέπει να είναι μια συμβολοσειρά UTF-8 με
κωδικοποίηση URL
που περιγράφει τη φύση του αιτήματος μεταφοράς.
Για παράδειγμα, αυτό μπορεί να είναι το όνομα ενός προϊόντος που αγοράζεται, ένα αναγνωριστικό παραγγελίας ή ένα ευχαριστήριο σημείωμα. Το πορτοφόλι πρέπει να αποκωδικοποιήσει το URL της τιμής και να εμφανίσει την αποκωδικοποιημένη τιμή στον χρήστη.
Υπόμνημα
Επιτρέπεται μία μόνο παράμετρος ερωτήματος memo ως προαιρετική. Η τιμή πρέπει
να είναι μια συμβολοσειρά UTF-8 με
κωδικοποίηση URL
που πρέπει να συμπεριληφθεί σε μια εντολή
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 απαιτείται ως pathname. Η τιμή πρέπει να είναι ένα υπό
συνθήκη
κωδικοποιημένο 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
σφάλματα client,
σφάλματα server,
και
αποκρίσεις ανακατεύθυνσης.
Η εφαρμογή πρέπει να απαντά με αυτές, ή με μια HTTP OK JSON απόκριση με σώμα:
{ "label": "<label>", "icon": "<icon>" }
Η τιμή <label> πρέπει να είναι ένα UTF-8 string που περιγράφει την πηγή του
αιτήματος συναλλαγής. Για παράδειγμα, αυτό θα μπορούσε να είναι το όνομα ενός
brand, καταστήματος, εφαρμογής ή ατόμου που κάνει το αίτημα.
Η τιμή <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
σφάλματα client,
σφάλματα server,
και
αποκρίσεις ανακατεύθυνσης.
Η εφαρμογή πρέπει να απαντά με αυτές, ή με μια 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 για να προτείνετε αλλαγές στην προδιαγραφή προκειμένου να ζητήσετε σχόλια από προγραμματιστές εφαρμογών και πορτοφολιών.
Ένα πραγματικό παράδειγμα τέτοιας πρότασης.
Δείτε Επίσης
- Προδιαγραφή Solana Pay v1.1 - Τελευταία έκδοση με βελτιώσεις
- Οδηγός Γρήγορης Έναρξης - Οδηγοί υλοποίησης
- Αποθετήριο GitHub - Υλοποιήσεις αναφοράς
Is this page helpful?