Μετάβαση στο περιεχόμενο
Τεκμηρίωση

Πρώτα βήματα

Το Payniko δέχεται πληρωμές με κάρτα μέσω ενός ενιαίου, υπογεγραμμένου REST API. Μπορείτε είτε να ανακατευθύνετε τον πληρωτή στη σελίδα πληρωμής του Payniko (Hosted API) είτε να συλλέγετε τα στοιχεία της κάρτας στη δική σας φόρμα πληρωμής (Direct API).

Βασικό URL και μορφές δεδομένων

Βασικό URL
https://payniko.com/api. Η διαδρομή δεν περιλαμβάνει αριθμό έκδοσης.
Μορφή
JSON με κωδικοποίηση UTF-8. Τα σφάλματα χρησιμοποιούν τη μορφή application/problem+json.
Ποσά
Συμβολοσειρές με δύο δεκαδικά ψηφία, για παράδειγμα "100.00". Μη στέλνετε ποτέ ποσά ως αριθμούς JSON.
Νόμισμα
USD, EUR, AED ή TRY. Αν το currency παραλειφθεί, ισχύει το κύριο νόμισμα του συστήματος.
Ημερομηνία και ώρα
ISO 8601 σε UTC με χιλιοστά του δευτερολέπτου, για παράδειγμα 2026-11-01T12:00:00.000Z.
Αναγνωριστικά
Τα αναγνωριστικά πληρωμών, επιστροφών, αναλήψεων και αμφισβητήσεων έχουν τη μορφή LUVEI-ABCD-123456. Αποθηκεύστε ολόκληρο το αναγνωριστικό.
Γλώσσα
Τα μηνύματα επιστρέφονται στη γλώσσα που ορίζει η κεφαλίδα Accept-Language (en, tr, de, ar, es, el)· προεπιλογή είναι το en. Οι κωδικοί και τα ονόματα πεδίων δεν μεταφράζονται ποτέ.

Απόκτηση κλειδιών API

Οι νέοι λογαριασμοί λαμβάνουν αυτόματα κλειδιά Sandbox και Live. Αν έχετε λογαριασμό, συνδεθείτε και ανοίξτε Κλειδιά πρόσβασης. Αν λάβατε αρχείο λογαριασμού, συνδεθείτε με το όνομα χρήστη και τον κωδικό του· δεν χρειάζεται νέα εγγραφή.

1. Δημιουργήστε τον λογαριασμό σας

  1. Δημιουργήστε λογαριασμό. Γράψτε το email που χρησιμοποιείτε και πατήστε Αποστολή κωδικού. Εισαγάγετε στη σελίδα εγγραφής τον εξαψήφιο κωδικό του email και πατήστε Επαλήθευση. Αν δεν έρθει, ελέγξτε τη διεύθυνση και τα ανεπιθύμητα· ζητήστε νέο κωδικό μετά την αντίστροφη μέτρηση.

  2. Μετά την επιβεβαίωση email, επιλέξτε κωδικό χώρας και γράψτε τον αριθμό που συνδέεται με τον δικό σας λογαριασμό Telegram. Πατήστε Επαλήθευση. Η επιβεβαίωση γίνεται στο Telegram, όχι με SMS.

  3. Ανοίξτε τον σύνδεσμο Telegram της σελίδας ή σαρώστε το QR με το τηλέφωνό σας αν είστε σε υπολογιστή. Πατήστε Έναρξη / Start στο bot και μοιραστείτε τον δικό σας αριθμό με το κουμπί του. Η πληκτρολόγηση αριθμού ως μήνυμα ή η αποστολή ξένης επαφής δεν αρκεί. Επιστρέψτε στην εγγραφή και περιμένετε την επιβεβαίωση.

  4. Πατήστε Συνέχεια στα στοιχεία λογαριασμού. Δημιουργήστε όνομα χρήστη και κωδικό πρόσβασης, επαναλάβετε τον κωδικό και ολοκληρώστε τις επιβεβαιώσεις. Πατήστε Δημιουργία λογαριασμού. Όνομα χρήστη: 4–32 χαρακτήρες, πεζά λατινικά γράμματα, ψηφία, τελεία, κάτω παύλα ή παύλα. Κωδικός: τουλάχιστον 12 χαρακτήρες.

  5. Αποθηκεύστε με ασφάλεια τον κωδικό ανάκτησης που εμφανίζεται μετά τη δημιουργία λογαριασμού. Εμφανίζεται μόνο σε αυτή την οθόνη και δεν είναι το μυστικό API. Επιβεβαιώστε την αποθήκευση και πατήστε Αποθήκευσα τον κωδικό, μετάβαση στον πίνακα.

2. Λάβετε κλειδιά Sandbox και κωδικό εμπόρου

  1. Ανοίξτε Κλειδιά πρόσβασης και βρείτε την κάρτα Sandbox. Οι νέοι λογαριασμοί έχουν ήδη κλειδιά. Αν παλιότερος λογαριασμός δεν έχει ενεργό κλειδί, προσθέστε προαιρετική ετικέτα και πατήστε Δημιουργία κλειδιού. Δεν απαιτείται καταχώριση IP.

  2. Πάρτε τις τρεις τιμές του πίνακα. Το εικονίδιο ματιού εμφανίζει το μυστικό και το εικονίδιο αντιγραφής το αντιγράφει. Ο κάτοχος μπορεί να δει ξανά ένα ενεργό μυστικό· δεν χρειάζεται ανανέωση κλειδιού μόνο για την προβολή.

  3. Δώστε τις τιμές με ασφαλή τρόπο στον προγραμματιστή σας ή αποθηκεύστε τις στις ιδιωτικές ρυθμίσεις του διακομιστή. Κλειδί και μυστικό πρέπει να προέρχονται από την ίδια κάρτα Sandbox. Μην βάλετε το μυστικό σε JavaScript του browser, δημόσια αρχεία ή URL.

ΤιμήΠού βρίσκεταιΧρήση
Κωδικός εμπόρουΕξαψήφιος κωδικός της κάρταςΚεφαλίδα X-Merchant-Code· ίδιος κωδικός λογαριασμού σε Sandbox και Live.
Κλειδί APIΓραμμή κλειδιού στην κάρτα SandboxΚεφαλίδα X-Api-Key· καθορίζει το περιβάλλον.
Μυστικό APIΕικονίδια ματιού/αντιγραφής στην ίδια κάρταΥπογραφή αιτημάτων και επαλήθευση webhook· δεν αποστέλλεται ως πεδίο αιτήματος.

Δημιουργία δοκιμαστικής πληρωμής

Δοκιμάστε την ενσωμάτωση στο Sandbox χωρίς πραγματική κίνηση χρημάτων. Η επιτυχής δοκιμή δεν αποτελεί προϋπόθεση για κλειδιά Live· συνιστάται για έλεγχο υπογραφών, ανακατευθύνσεων και webhooks.

  1. Χρησιμοποιήστε παράδειγμα διακομιστή από Βοηθητικές συναρτήσεις αιτημάτων με κλειδί Sandbox, μυστικό Sandbox και κωδικό εμπόρου. Δεν υπάρχει διαφορετικό domain API Sandbox: χρησιμοποιήστε τη βασική URL της τεκμηρίωσης. Το κλειδί καθορίζει το περιβάλλον.

  2. Ελέγξτε τα όρια ποσών και νομίσματα Sandbox στο Εμπειρία πληρωμής. Επιλέξτε επιτρεπτό ποσό που δεν τελειώνει σε .77: αυτή η κατάληξη ενεργοποιεί σενάριο εκκρεμούς αποτελέσματος.

  3. Συμπληρώστε το παράδειγμα POST /api/payments/hosted: μοναδικό merchant_order_id, ποσό, νόμισμα, customer.email / customer.phone και τρεις URL ειδοποίησης/επιστροφής. Χρησιμοποιήστε προσβάσιμα στοιχεία και τηλέφωνο με κωδικό χώρας. Το webhook_url πρέπει να είναι δημόσιο HTTPS ακόμη και στο Sandbox· localhost και ιδιωτικά δίκτυα δεν επιτρέπονται. Τα success_url / fail_url είναι οι URL επιστροφής του browser.

  4. Στείλτε το υπογεγραμμένο POST /api/payments/hosted από τον διακομιστή. Για νέα δοκιμή χρησιμοποιήστε νέα αναφορά παραγγελίας και νέο Idempotency-Key· για επανάληψη ίδιου αιτήματος μετά από σφάλμα σύνδεσης κρατήστε το ίδιο. Το HTTP 201 σημαίνει δημιουργία πληρωμής, όχι επιτυχή πληρωμή.

  5. Ανοίξτε ολόκληρο το next_action.url της απάντησης σε browser. Στη φόρμα Sandbox χρησιμοποιήστε τη δοκιμαστική κάρτα 4000000000000002, μελλοντική λήξη και τριψήφιο CVV, π.χ. 12/30 και 123. Ολοκληρώστε επιτυχώς τη δοκιμαστική οθόνη 3D Secure. Μην χρησιμοποιήσετε πραγματική κάρτα. Άλλα σενάρια: Δοκιμαστικές κάρτες.

  6. Μετά την επιστροφή, ελέγξτε με GET /api/payments/{payment_id} ή επεξεργαστείτε ένα webhook αφού επαληθεύσετε την υπογραφή του. Η κατάσταση API πρέπει να είναι succeeded. Τα requires_action / processing είναι ενδιάμεσα· το failed δεν μετρά ως επιτυχία. Η σελίδα επιτυχίας μόνη της δεν αποδεικνύει πληρωμή.

  7. Ελέγξτε το αποτέλεσμα στο Αρχείο αιτημάτων. Μια επιτυχής εγγραφή ολοκληρώνει αυτό το σενάριο δοκιμής. Το Live χρησιμοποιεί ξεχωριστό ζεύγος κλειδιών.

Απόκτηση κλειδιών Live

Το Sandbox και το Live χρησιμοποιούν ξεχωριστά ζεύγη κλειδιών. Τα κλειδιά Live δεν απαιτούν IP ή επιτυχή πληρωμή Sandbox. Η αλλαγή περιβάλλοντος στον πίνακα δεν αλλάζει τη ρύθμιση του διακομιστή.

  1. Ανοίξτε Πρόσβαση Live και την κάρτα Παραγωγή. Οι νέοι λογαριασμοί έχουν ήδη κλειδιά Live· αν παλιότερος λογαριασμός δεν έχει, πατήστε Δημιουργία κλειδιού.

  2. Πάρτε το κλειδί API και το μυστικό από την ίδια κάρτα Live. Ο κωδικός εμπόρου παραμένει ίδιος. Αποθηκεύστε το μυστικό μόνο στην ιδιωτική ρύθμιση του διακομιστή.

  3. Βάλτε αυτό το ζεύγος Live στις ρυθμίσεις του παραγωγικού διακομιστή. Η URL API παραμένει ίδια και τα Sandbox κλειδιά εξακολουθούν να λειτουργούν για δοκιμές. Η προβολή κάρτας στο panel δεν αλλάζει τις ρυθμίσεις του διακομιστή.

  4. Ελέγξτε τα όρια και νομίσματα Live στο Εμπειρία πληρωμής. Τα κλειδιά είναι έτοιμα. Τα αιτήματα Live μπαίνουν στην πραγματική ροή χρέωσης· συνεχίστε τις δοκιμές καρτών με Sandbox κλειδιά.

Αν κολλήσετε σε κάποιο βήμα

ΚατάστασηΕπόμενο βήμα
Δεν επιβεβαιώνεται email ή TelegramΒάλτε τον κωδικό email στην εγγραφή. Ανοίξτε το bot από τον εκεί σύνδεσμο και μοιραστείτε τον ίδιο δικό σας αριθμό με το κουμπί του. Ανανεώστε ληγμένο σύνδεσμο από την εγγραφή.
Δεν δημιουργείται κλειδίΕλέγξτε ότι ο λογαριασμός είναι ενεργός και οι εισπράξεις API επιτρέπονται. Δεν απαιτείται IP ή πληρωμή Sandbox.
Λείπει το κλειδί LiveΑνοίξτε την κάρτα Live. Οι νέοι λογαριασμοί έχουν ήδη κλειδιά· δημιουργήστε ένα αν ο παλιότερος λογαριασμός δεν έχει ενεργό. Αν συνεχιστεί, Επικοινωνήστε με την υποστήριξη.
Δεν αποθηκεύσατε το μυστικόΧρησιμοποιήστε το μάτι ή την αντιγραφή της ενεργής κάρτας. Ο κωδικός ανάκτησης δεν είναι το μυστικό API.
Δεν εμφανίζονται εισπράξεις APIΕπικοινωνήστε με την υποστήριξη για έλεγχο πρόσβασης. Οι σύνδεσμοι πληρωμής και το API μπορεί να ενεργοποιούνται χωριστά.

Επισκόπηση endpoint

EndpointΣκοπός
POST /api/payments/hostedΔημιουργία πληρωμής Hosted API · Hosted API
GET /api/payments/{payment_id}Ανάκτηση πληρωμής · Hosted API · Direct API
GET /api/payments/bin/{bin6}Έλεγχος BIN (πρώτα έξι ψηφία της κάρτας) · Direct API
POST /api/payments/directΔημιουργία πληρωμής Direct API · Direct API
GET /api/account/balanceΥπόλοιπο λογαριασμού · Υπόλοιπα
GET /api/account/available-balanceΥπόλοιπο προς ανάληψη · Υπόλοιπα
POST|GET /api/account/refundsΕπιστροφές χρημάτων και ακυρώσεις · Επιστροφές χρημάτων
POST|GET /api/account/withdrawalsΑναλήψεις USDT · Αναλήψεις
GET /api/account/disputesΑμφισβητήσεις και απαντήσεις σε αμφισβητήσεις · Αμφισβητήσεις
GET|POST /api/webhooks/deliveriesΙστορικό αποστολών webhook και επαναποστολή · Αποστολές
POST /api/sandbox/disputesΔημιουργία δοκιμαστικής αμφισβήτησης (μόνο με κλειδί sandbox) · Sandbox

Όλα τα endpoint χρησιμοποιούν τον ίδιο έλεγχο ταυτότητας και κάθε POST απαιτεί επιπλέον Idempotency-Key.

Έλεγχος ταυτότητας

Κάθε αίτημα περιέχει το κλειδί API σας και υπογραφή HMAC-SHA256 που υπολογίζεται με το μυστικό κλειδί API σας.

Κλειδί API και μυστικό κλειδί

ΤιμήΜορφήΠεριγραφή
Κλειδί APIapi_ + 32 hexΑποστέλλεται στην κεφαλίδα X-Api-Key. Το κλειδί καθορίζει επίσης το περιβάλλον.
Μυστικό κλειδί API64 hexΥπογράφει αιτήματα και επαληθεύει υπογραφές webhook· δεν αποστέλλεται με το αίτημα. Ο κάτοχος μπορεί να δει ή να αντιγράψει ξανά ενεργό μυστικό από τα αντίστοιχα εικονίδια του panel.
Κωδικός εμπόρου[0-9]{6}Ο αριθμός του λογαριασμού σας, που αποστέλλεται στην κεφαλίδα X-Merchant-Code.

Κάθε κλειδί API ανήκει σε ένα μόνο περιβάλλον, sandbox ή παραγωγής, και το κλειδί καθορίζει το περιβάλλον στο οποίο εκτελείται ένα αίτημα: χρησιμοποιήστε το κλειδί sandbox για δοκιμές και το κλειδί παραγωγής για πραγματικές πληρωμές. Η κεφαλίδα X-Environment είναι προαιρετική· αν παραλειφθεί, το αίτημα εκτελείται στο περιβάλλον του κλειδιού. Αν αποσταλεί, η τιμή της πρέπει να είναι sandbox ή live (χωρίς διάκριση πεζών-κεφαλαίων) και να αντιστοιχεί στο περιβάλλον του κλειδιού· διαφορετικά, το αίτημα απορρίπτεται με HTTP 401 και κωδικό 0055502 και δεν εκτελείται καμία επεξεργασία. Οι υποχρεωτικές κεφαλίδες ελέγχου ταυτότητας είναι οι X-Api-Key, X-Merchant-Code, X-Timestamp και X-Signature, καθώς και η Idempotency-Key σε κάθε αίτημα εγγραφής. Το περιβάλλον δεν αποτελεί μέρος της συμβολοσειράς προς υπογραφή. Κάθε σώμα απάντησης που περιέχει το πεδίο environment (πληρωμές, υπόλοιπα, επιστροφές χρημάτων και άλλοι πόροι) αναφέρει το περιβάλλον που χρησιμοποιήθηκε.

Ανανέωση κλειδιού

  • Δημιουργήστε νέο κλειδί αν το μυστικό κλειδί ενδέχεται να έχει εκτεθεί ή όταν αποχωρεί κάποιο πρόσωπο που είχε πρόσβαση σε αυτό. Το προηγούμενο κλειδί παύει να λειτουργεί αμέσως.
  • Οι ειδοποιήσεις webhook και οι σύνδεσμοι επιστροφής μιας πληρωμής υπογράφονται με το μυστικό κλειδί που αντιστοιχεί στο κλειδί API με το οποίο δημιουργήθηκε η πληρωμή· αυτό ισχύει και για ειδοποιήσεις επιστροφών χρημάτων και αμφισβητήσεων που αποστέλλονται πολύ μετά την πληρωμή. Διατηρήστε το προηγούμενο μυστικό κλειδί για όσο διάστημα ενδέχεται να φτάσουν ακόμη τέτοια συμβάντα.

Κεφαλίδες αιτήματος

ΚεφαλίδαΥποχρεωτικόΤιμή
X-Api-KeyΥποχρεωτικόΤο κλειδί API σας.
X-Merchant-CodeΥποχρεωτικόΟ εξαψήφιος κωδικός εμπόρου σας.
X-TimestampΥποχρεωτικόΗ τρέχουσα ώρα Unix σε δευτερόλεπτα (10 ψηφία).
X-SignatureΥποχρεωτικόΗ υπογραφή του αιτήματος: 64 δεκαεξαδικοί χαρακτήρες με πεζά.
Idempotency-KeyΥπό προϋποθέσειςΥποχρεωτική σε κάθε POST. Δείτε Idempotency-Key.
Content-TypeΥπό προϋποθέσειςapplication/json για αιτήματα με σώμα.
Accept-LanguageΠροαιρετικόΓλώσσα των μηνυμάτων σφάλματος: en (προεπιλογή), tr, de, ar, es ή el.

Στείλτε κάθε κεφαλίδα X- ακριβώς μία φορά. Οι τιμές των κεφαλίδων δεν πρέπει να περιέχουν κόμματα, αλλαγές γραμμής ή κενά.

Υπογραφή αιτημάτων

Η συμβολοσειρά προς υπογραφή αποτελείται από τέσσερις τιμές που ενώνονται με χαρακτήρα αλλαγής γραμμής (line feed): τη χρονική σήμανση, τη μέθοδο HTTP, τη διαδρομή και την τιμή κατακερματισμού SHA-256 του σώματος.

Συμβολοσειρά προς υπογραφή

canonical   = X-Timestamp + "\n" + METHOD + "\n" + path + "\n" + hex(sha256(raw body))
X-Signature = hex(HMAC-SHA256(key = API secret, message = canonical))   // 64 lowercase hex characters
  1. Κωδικοποιήστε το σώμα σε JSON μία φορά και υπολογίστε την τιμή κατακερματισμού SHA-256 ακριβώς αυτών των byte (δεκαεξαδική μορφή με πεζά). Για αίτημα χωρίς σώμα, υπολογίστε την τιμή κατακερματισμού της κενής συμβολοσειράς (e3b0c442…b855).

  2. Χρησιμοποιήστε τη διαδρομή ακριβώς όπως εμφανίζεται στο URL, ξεκινώντας από /api/ και χωρίς τη συμβολοσειρά ερωτήματος (query string). Για παράδειγμα, για το GET /api/payments/bin/540061?amount=100.00 υπογράφεται το /api/payments/bin/540061.

  3. Υπολογίστε το HMAC-SHA256 με κλειδί το μυστικό κλειδί API. Χρησιμοποιήστε τη συμβολοσειρά του μυστικού κλειδιού ως έχει· μην την αποκωδικοποιείτε.

  4. Στείλτε το αποτέλεσμα στην κεφαλίδα X-Signature και, ως σώμα του αιτήματος, ακριβώς τα byte για τα οποία υπολογίσατε την τιμή κατακερματισμού.

Ανοχή χρονικής σήμανσης

Η τιμή X-Timestamp πρέπει να απέχει έως 300 δευτερόλεπτα από την ώρα διακομιστή του Payniko, προς οποιαδήποτε κατεύθυνση. Διατηρείτε το ρολόι του διακομιστή σας συγχρονισμένο μέσω NTP. Αν η απόκλιση είναι μεγαλύτερη, το αίτημα απορρίπτεται με 401 0055504 και το detail περιέχει την ώρα διακομιστή του Payniko.

Παράδειγμα υπογραφής

Χρησιμοποιήστε αυτές τις ενδεικτικές τιμές για να ελέγξετε την υλοποίησή σας. Με αυτό το μυστικό κλειδί, αυτή τη χρονική σήμανση και αυτό το σώμα, ο κώδικάς σας πρέπει να παράγει ακριβώς αυτή την υπογραφή.

Μυστικό κλειδί API (ενδεικτικό)

e3c4a5b6d7e8f90112233445566778899aabbccddeeff0011223344556677889

Σώμα (ακριβή byte)

{"merchant_order_id":"ORDER-2026-001","amount":"100.00","currency":"USD","webhook_url":"https://merchant.example/webhooks/payments","success_url":"https://merchant.example/payment/success","fail_url":"https://merchant.example/payment/failure","customer":{"email":"[email protected]","phone":"+37061234567"}}

sha256(body)

dca2bae6e50c13c26b22b57e3c31161ba3174147060fcbf22b2ba99cc13560ae

Συμβολοσειρά προς υπογραφή

1793534400
POST
/api/payments/hosted
dca2bae6e50c13c26b22b57e3c31161ba3174147060fcbf22b2ba99cc13560ae

X-Signature

5522c27ff0b49ffa4c44712ed5db350c1c5a2cbfc0c568edbce39cff59ca620c

Κεφαλίδες αιτήματος

POST /api/payments/hosted
X-Api-Key: api_0123456789abcdef0123456789abcdef
X-Merchant-Code: 123456
X-Timestamp: 1793534400
X-Signature: 5522c27ff0b49ffa4c44712ed5db350c1c5a2cbfc0c568edbce39cff59ca620c
Idempotency-Key: 6f1c2a9e-1d2b-4c1a-9f0e-3b7d5a2c8e41
Content-Type: application/json
Accept: application/json

Βοηθητικές συναρτήσεις αιτημάτων

Προσθέστε μία από αυτές τις βοηθητικές συναρτήσεις στον διακομιστή σας. Η συνάρτηση διαβάζει τα API_KEY, API_SECRET και MERCHANT_CODE από μεταβλητές περιβάλλοντος, υπογράφει το αίτημα και το αποστέλλει. Τα υπόλοιπα παραδείγματα κώδικα χρησιμοποιούν αυτή τη βοηθητική συνάρτηση.

#!/usr/bin/env bash
# payniko.sh - signed request helper (bash, curl, openssl).
# Usage: ./payniko.sh METHOD PATH [JSON_BODY] [IDEMPOTENCY_KEY]
# Reads API_KEY, API_SECRET and MERCHANT_CODE. The key decides sandbox or live.
# Optional: SANDBOX_SCENARIO adds the X-Sandbox-Scenario header (sandbox keys only).
set -euo pipefail
: "${API_KEY:?set API_KEY}" "${API_SECRET:?set API_SECRET}" "${MERCHANT_CODE:?set MERCHANT_CODE}"
BASE_URL="https://payniko.com"
method="$(printf '%s' "${1:?METHOD}" | tr '[:lower:]' '[:upper:]')"
target="${2:?PATH}"            # may include a query string; only the path is signed
body="${3-}"                   # sent byte-for-byte as hashed
idempotency_key="${4-}"
path="${target%%\?*}"
timestamp="$(date +%s)"
body_hash="$(printf '%s' "$body" | openssl dgst -sha256 | sed 's/^.* //')"
signature="$(printf '%s\n%s\n%s\n%s' "$timestamp" "$method" "$path" "$body_hash" \
  | openssl dgst -sha256 -hmac "$API_SECRET" | sed 's/^.* //')"
args=(-sS --fail-with-body -X "$method" "$BASE_URL$target"
  -H "X-Api-Key: $API_KEY" -H "X-Merchant-Code: $MERCHANT_CODE" -H "X-Timestamp: $timestamp"
  -H "X-Signature: $signature" -H "Accept: application/json" -H "Accept-Language: en")
if [ -n "$body" ]; then args+=(-H "Content-Type: application/json" --data-binary "$body"); fi
if [ -n "$idempotency_key" ]; then args+=(-H "Idempotency-Key: $idempotency_key"); fi
if [ -n "${SANDBOX_SCENARIO-}" ]; then args+=(-H "X-Sandbox-Scenario: $SANDBOX_SCENARIO"); fi
curl "${args[@]}"
echo
// payniko.mjs - signed request helper (Node.js 20+, no dependencies).
import {createHash, createHmac, randomUUID} from 'node:crypto';

const base = 'https://payniko.com';
const key = process.env.API_KEY ?? '';
const secret = process.env.API_SECRET ?? '';
const merchant = process.env.MERCHANT_CODE ?? '';
if (!/^api_[0-9a-f]{32}$/.test(key) || !/^[0-9a-f]{64}$/.test(secret) || !/^[0-9]{6}$/.test(merchant)) {
  throw new Error('Set API_KEY, API_SECRET and MERCHANT_CODE.');
}

export async function api(method, path, body, idempotencyKey, extraHeaders = {}) {
  const url = new URL(path, base);
  if (url.origin !== base || !url.pathname.startsWith('/api/')) throw new Error('Invalid API URL');
  method = method.toUpperCase();
  const raw = body === undefined ? '' : JSON.stringify(body);   // hash and send the same bytes
  const timestamp = Math.floor(Date.now() / 1000).toString();
  const canonical = [timestamp, method, url.pathname,
    createHash('sha256').update(raw, 'utf8').digest('hex')].join('\n');
  const signature = createHmac('sha256', secret).update(canonical, 'utf8').digest('hex');
  const response = await fetch(url, {
    method,
    redirect: 'error',
    headers: {
      ...extraHeaders,
      'X-Api-Key': key, 'X-Merchant-Code': merchant,
      'X-Timestamp': timestamp, 'X-Signature': signature,
      'Accept': 'application/json', 'Accept-Language': 'en',
      ...(raw ? {'Content-Type': 'application/json'} : {}),
      ...(idempotencyKey ? {'Idempotency-Key': idempotencyKey} : {}),
    },
    ...(raw ? {body: raw} : {}),
  });
  const text = await response.text();
  const data = text ? JSON.parse(text) : null;
  if (!response.ok) {
    throw Object.assign(new Error(`${response.status} ${data?.code ?? ''} ${data?.detail ?? ''}`.trim()),
      {status: response.status, problem: data, correlationId: response.headers.get('x-correlation-id')});
  }
  return data;
}
export {randomUUID};
<?php
// payniko.php - signed request helper (PHP 8.1+ with the curl extension).
function payniko_api(string $method, string $path, ?array $body = null, ?string $idempotencyKey = null, array $extraHeaders = []): ?array
{
    $base = 'https://payniko.com';
    $key = getenv('API_KEY') ?: '';
    $secret = getenv('API_SECRET') ?: '';
    $merchant = getenv('MERCHANT_CODE') ?: '';
    if (!preg_match('/^api_[0-9a-f]{32}$/', $key) || !preg_match('/^[0-9a-f]{64}$/', $secret)
        || !preg_match('/^[0-9]{6}$/', $merchant)) {
        throw new RuntimeException('Set API_KEY, API_SECRET and MERCHANT_CODE.');
    }
    $method = strtoupper($method);
    // Hash and send the same bytes.
    $raw = $body === null ? '' : json_encode($body, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
    $timestamp = (string) time();
    $canonical = implode("\n", [$timestamp, $method, explode('?', $path, 2)[0], hash('sha256', $raw)]);
    $headers = array_merge($extraHeaders, [
        'X-Api-Key: ' . $key,
        'X-Merchant-Code: ' . $merchant,
        'X-Timestamp: ' . $timestamp,
        'X-Signature: ' . hash_hmac('sha256', $canonical, $secret),
        'Accept: application/json',
        'Accept-Language: en',
    ]);
    if ($raw !== '') $headers[] = 'Content-Type: application/json';
    if ($idempotencyKey !== null) $headers[] = 'Idempotency-Key: ' . $idempotencyKey;
    $ch = curl_init($base . $path);
    curl_setopt_array($ch, [
        CURLOPT_CUSTOMREQUEST => $method,
        CURLOPT_HTTPHEADER => $headers,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_FOLLOWLOCATION => false,
        CURLOPT_TIMEOUT => 60,
    ]);
    if ($raw !== '') curl_setopt($ch, CURLOPT_POSTFIELDS, $raw);
    $response = curl_exec($ch);
    if ($response === false) throw new RuntimeException('Network error: ' . curl_error($ch));
    $status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
    $data = $response === '' ? null : json_decode($response, true);
    if ($status >= 400) {
        throw new RuntimeException($status . ' ' . ($data['code'] ?? '') . ' ' . ($data['detail'] ?? ''), $status);
    }
    return $data;
}
# payniko.py - signed request helper (Python 3.9+, standard library only).
import hashlib, hmac, json, os, re, time, urllib.error, urllib.request

BASE_URL = "https://payniko.com"
KEY = os.environ.get("API_KEY", "")
SECRET = os.environ.get("API_SECRET", "")
MERCHANT = os.environ.get("MERCHANT_CODE", "")
if not (re.fullmatch(r"api_[0-9a-f]{32}", KEY) and re.fullmatch(r"[0-9a-f]{64}", SECRET)
        and re.fullmatch(r"[0-9]{6}", MERCHANT)):
    raise SystemExit("Set API_KEY, API_SECRET and MERCHANT_CODE.")


def api(method, path, body=None, idempotency_key=None, extra_headers=None):
    method = method.upper()
    # Hash and send the same bytes.
    raw = b"" if body is None else json.dumps(body, separators=(",", ":"), ensure_ascii=False).encode("utf-8")
    timestamp = str(int(time.time()))
    canonical = "\n".join([timestamp, method, path.split("?", 1)[0], hashlib.sha256(raw).hexdigest()])
    signature = hmac.new(SECRET.encode(), canonical.encode(), hashlib.sha256).hexdigest()
    headers = dict(extra_headers or {})
    headers.update({
        "X-Api-Key": KEY, "X-Merchant-Code": MERCHANT,
        "X-Timestamp": timestamp, "X-Signature": signature,
        "Accept": "application/json", "Accept-Language": "en",
    })
    if raw:
        headers["Content-Type"] = "application/json"
    if idempotency_key:
        headers["Idempotency-Key"] = idempotency_key
    request = urllib.request.Request(BASE_URL + path, data=raw or None, method=method, headers=headers)
    try:
        with urllib.request.urlopen(request, timeout=60) as response:
            text = response.read()
            return json.loads(text) if text else None
    except urllib.error.HTTPError as error:
        problem = json.loads(error.read() or b"{}")
        raise RuntimeError(f"{error.code} {problem.get('code')} {problem.get('detail')}") from None

Επαλήθευση της ρύθμισης

Η ανάκτηση του υπολοίπου είναι ασφαλής πρώτη κλήση, επειδή δεν δημιουργεί τίποτα.

./payniko.sh GET '/api/account/balance'
import {api} from './payniko.mjs';

const result = await api('GET', '/api/account/balance');
console.log(result);
<?php
require __DIR__ . '/payniko.php';

$result = payniko_api('GET', '/api/account/balance');
print_r($result);
from payniko import api

result = api("GET", "/api/account/balance")
print(result)

Οι επαναλαμβανόμενες μη έγκυρες υπογραφές κλειδώνουν προσωρινά το κλειδί: 20 αποτυχίες από την ίδια διεύθυνση μέσα σε 15 λεπτά το κλειδώνουν για 15 λεπτά και 300 αποτυχίες μέσα σε μία ώρα το κλειδώνουν για μία ώρα (429 1000005 με Retry-After). Διορθώστε τη λογική υπογραφής πριν δοκιμάσετε ξανά.

Idempotency-Key

Κάθε POST απαιτεί Idempotency-Key. Ένα αίτημα που αποστέλλεται ξανά με το ίδιο κλειδί εκτελείται μόνο μία φορά και επιστρέφει το αρχικό αποτέλεσμα· έτσι, η επανάληψη μετά από λήξη χρονικού ορίου είναι ασφαλής.

  • Δημιουργήστε νέο τυχαίο κλειδί, για παράδειγμα ένα UUID, για κάθε νέα λειτουργία και αποθηκεύστε το μαζί με την παραγγελία σας πριν στείλετε το αίτημα.
  • Μετά από λήξη χρονικού ορίου ή σφάλμα δικτύου, στείλτε ξανά το ίδιο σώμα με το ίδιο κλειδί.
  • Μην επαναχρησιμοποιείτε ποτέ ένα κλειδί με διαφορετικό σώμα· ένα τέτοιο αίτημα απορρίπτεται με 409 (9000010 ή 0055503 στο Direct API).
  • Αν το αρχικό αίτημα βρίσκεται ακόμη σε εξέλιξη, η απάντηση είναι 409: 9000039 στο Hosted API (με integration.retryable: true) ή 0055503 με metadata.retry_class: retry_same_request στο Direct API. Στείλτε ξανά το ίδιο αίτημα μετά από σύντομη αναμονή.
  • Μορφή: 8–128 χαρακτήρες από το σύνολο A-Z a-z 0-9 _ -. Αν το κλειδί λείπει ή δεν είναι έγκυρο, επιστρέφεται 422 1000009.
  • Ένα υπογεγραμμένο αίτημα (X-Timestamp και X-Signature) δεσμεύεται για 10 λεπτά στο πρώτο Idempotency-Key με το οποίο στάλθηκε· η ίδια υπογραφή με διαφορετικό κλειδί απορρίπτεται με 409 9000010. Δημιουργήστε νέα χρονοσφραγίδα και υπογραφή για κάθε νέα ενέργεια. Αν αυτός ο έλεγχος δεν είναι προσωρινά διαθέσιμος, η απάντηση είναι 503 1000011.

Απαιτήσεις αιτημάτων

  • Στέλνετε μόνο τα πεδία που περιγράφονται στην τεκμηρίωση. Τα άγνωστα πεδία απορρίπτονται με 422 1000001.
  • Τα αιτήματα POST δεν πρέπει να περιέχουν συμβολοσειρά ερωτήματος. Τα αιτήματα GET δεν πρέπει να περιέχουν σώμα και δέχονται μόνο τις παραμέτρους ερωτήματος που περιγράφονται στην τεκμηρίωση.
  • Το σώμα πρέπει να είναι ένα μόνο αντικείμενο JSON με βάθος ένθεσης έως 7 επίπεδα, συμπεριλαμβανομένου του αντικειμένου ανώτατου επιπέδου.
  • Τα όρια ρυθμού ισχύουν ανά διεύθυνση IP (600 αιτήματα ανά λεπτό πριν από την ταυτοποίηση) και ανά κλειδί (300 αιτήματα ανά λεπτό). Σε 429 (1000005), περιμένετε το διάστημα Retry-After και δοκιμάστε ξανά με αυξανόμενη αναμονή. Κάθε απάντηση περιέχει X-RateLimit-Limit και X-RateLimit-Remaining.
ΚεφαλίδαΣημασία
X-Correlation-IdΜοναδικό αναγνωριστικό του αιτήματος. Καταγράψτε το και συμπεριλάβετέ το στα αιτήματα υποστήριξης.
Retry-AfterΑριθμός δευτερολέπτων αναμονής. Υπάρχει πάντα σε 429· σε 503 υπάρχει όταν είναι γνωστός.
X-RateLimit-LimitΌριο αιτημάτων του τρέχοντος παραθύρου. Δεν υπάρχει κεφαλίδα X-RateLimit-Reset.
X-RateLimit-RemainingΑιτήματα που απομένουν στο τρέχον χρονικό διάστημα.
Content-LanguageΓλώσσα των μηνυμάτων της απάντησης.

Σφάλματα ελέγχου ταυτότητας

ΚωδικόςHTTPΣημασίαΣυνιστώμενη ενέργεια
0055502401 / 403Η πρόσβαση απορρίφθηκε. 401: λείπει η κεφαλίδα X-Api-Key ή X-Merchant-Code, μια κεφαλίδα έχει σταλεί δύο φορές ή έχει μη έγκυρη μορφή, το κλειδί είναι άγνωστο ή έχει ανακληθεί, ο κωδικός εμπόρου δεν αντιστοιχεί ή στάλθηκε X-Environment που δεν αντιστοιχεί στο κλειδί. 403: ο λογαριασμός ή η πρόσβαση στο Direct API δεν είναι ενεργή.Επαληθεύστε τις κεφαλίδες και το κλειδί API. Μην επαναλαμβάνετε το αίτημα αυτόματα.
0055504401Η κεφαλίδα X-Timestamp ή X-Signature λείπει ή έχει μη έγκυρη μορφή, ή το ρολόι του διακομιστή σας αποκλίνει από την ώρα διακομιστή του Payniko περισσότερο από 300 δευτερόλεπτα.Διορθώστε το ρολόι του διακομιστή σας (NTP) και υπογράψτε ξανά το αίτημα με τρέχουσα χρονική σήμανση.
0055505401Η υπογραφή δεν αντιστοιχεί στο αίτημα.Υπολογίστε την τιμή κατακερματισμού ακριβώς των byte του σώματος που αποστέλλετε, υπογράψτε τη διαδρομή χωρίς τη συμβολοσειρά ερωτήματος και χρησιμοποιήστε το σωστό μυστικό κλειδί API.
9000002401Το αίτημα είχε ήδη λήξει τη στιγμή της επεξεργασίας του.Υπογράψτε ξανά το αίτημα με τρέχουσα χρονική σήμανση και στείλτε το εκ νέου.
1000009422Η κεφαλίδα Idempotency-Key λείπει ή δεν είναι έγκυρη.Σε κάθε POST στείλτε κλειδί 8–128 χαρακτήρων από το σύνολο A-Z a-z 0-9 _ -.
9000010409Αυτό το Idempotency-Key έχει ήδη χρησιμοποιηθεί για διαφορετικό αίτημα ή το ίδιο υπογεγραμμένο αίτημα στάλθηκε ξανά με διαφορετικό κλειδί.Επαναλάβετε το αίτημα με το ίδιο κλειδί και το ίδιο σώμα· χρησιμοποιήστε νέο κλειδί μόνο για νέα λειτουργία.
1000005429Υπερβολικά πολλά αιτήματα ή προσωρινό κλείδωμα μετά από επανειλημμένες αποτυχίες υπογραφής.Περιμένετε το διάστημα που ορίζει το Retry-After και, στη συνέχεια, επαναλάβετε το αίτημα με προοδευτικά αυξανόμενη αναμονή.

Hosted API

Ο διακομιστής σας δημιουργεί την πληρωμή και ανακατευθύνει τον πληρωτή στη σελίδα πληρωμής του Payniko. Το Payniko συλλέγει τα στοιχεία της κάρτας και εκτελεί τον έλεγχο 3-D Secure, ώστε τα δεδομένα κάρτας να μη φτάνουν ποτέ στα συστήματά σας. Το Hosted API είναι η απλούστερη επιλογή ενσωμάτωσης.

Βήματα ενσωμάτωσης

  1. Δημιουργία της πληρωμής

    Ο διακομιστής σας καλεί το POST /api/payments/hosted. Αποθηκεύστε το payment_id που επιστρέφεται μαζί με την παραγγελία σας.

  2. Ανακατεύθυνση του πληρωτή

    Ανακατευθύνετε το πρόγραμμα περιήγησης στο next_action.url ή ανοίξτε το σε νέα καρτέλα· μην το φορτώνετε ποτέ μέσα σε iframe.

  3. Ολοκλήρωση της πληρωμής από τον πληρωτή

    Ο πληρωτής εισάγει τα στοιχεία της κάρτας. Αν η τράπεζα απαιτεί 3-D Secure, η σελίδα το εμφανίζει. Μια ενδιάμεση απόρριψη σε αυτό το στάδιο δεν είναι οριστική· η πληρωμή καταλήγει σε τελική κατάσταση χωρίς καμία ενέργεια από την πλευρά σας.

  4. Επιστροφή του πληρωτή

    Η σελίδα εμφανίζει το αποτέλεσμα με σύντομη αντίστροφη μέτρηση και, στη συνέχεια, ανακατευθύνει το πρόγραμμα περιήγησης με υπογεγραμμένες παραμέτρους στο success_url (μόνο αν η πληρωμή είναι succeeded) ή στο fail_url. Δείτε URL επιστροφής.

  5. Επιβεβαίωση του αποτελέσματος

    Περιμένετε την ειδοποίηση webhook ή καλέστε το GET /api/payments/{payment_id}. Εκτελέστε την παραγγελία μόνο όταν η κατάσταση είναι succeeded.

Ροή πληρωμής

Your server                          Payniko                        Payer's browser
 1  POST /api/payments/hosted  ─────►  201  status: requires_action
                               ◄─────  next_action.url
 2  redirect (303) ───────────────────────────────────────────────►  payment page
 3                                     card + 3-D Secure  ◄────────  payer pays
 4                                     ───────────────────────────►  success_url / fail_url
                                                                     (signed parameters)
 5  webhook  ◄───────────────────────  payment.succeeded / payment.failed
    GET /api/payments/{payment_id} ──►  current status
POST/api/payments/hostedΑπαιτείται Idempotency-Key

Δημιουργία πληρωμής

Επιστρέφει 201 με τη νέα πληρωμή. Η εκ νέου αποστολή του ίδιου αιτήματος με το ίδιο Idempotency-Key επιστρέφει 200 με την ίδια πληρωμή.

Σώμα αιτήματος

ΠεδίοΤύποςΥποχρεωτικόΠεριγραφήΠαράδειγμα
merchant_order_idstringΥποχρεωτικόΟ κωδικός της παραγγελίας σας: 1–64 χαρακτήρες από το σύνολο A-Z a-z 0-9 . _ -. Κάθε κωδικός μπορεί να χρησιμοποιηθεί για μία πληρωμή ανά περιβάλλον, ακόμη και αν η πληρωμή αυτή απέτυχε ή έληξε. Η μοναδικότητα ελέγχεται χωρίς διάκριση πεζών-κεφαλαίων: τα ORDER-1 και order-1 θεωρούνται ο ίδιος κωδικός.ORDER-2026-001
amountstringΥποχρεωτικόΘετικό ποσό με δύο δεκαδικά ψηφία και χωρίς αρχικά μηδενικά, για παράδειγμα "100.00". Πρέπει να είναι εντός των ορίων του λογαριασμού σας."100.00"
currencystringΠροαιρετικόΑν παραλειφθεί, ισχύει το κύριο νόμισμα του συστήματος.
USDEURAEDTRY
USD
webhook_urlstring · uriΥποχρεωτικόΤελικό σημείο που λαμβάνει τα webhooks αυτής της πληρωμής. Δημόσιο HTTPS στη θύρα 443, έως 500 χαρακτήρες, χωρίς στοιχεία χρήστη ή fragment. Ιδιωτικές και τοπικές διευθύνσεις δεν γίνονται δεκτές, ούτε στο sandbox· το όνομα κεντρικού υπολογιστή πρέπει να επιλύεται σε δημόσια διεύθυνση IP τη στιγμή του αιτήματος, αλλιώς 422 9000008.https://merchant.example/webhooks/payments
success_urlstring · uriΥποχρεωτικόΠροορισμός ανακατεύθυνσης του προγράμματος περιήγησης μετά από επιτυχή πληρωμή. HTTPS, έως 2048 χαρακτήρες, χωρίς στοιχεία χρήστη (userinfo) ή αναγνωριστικό τμήματος (fragment). Το sandbox δέχεται επίσης http://localhost, 127.0.0.1 και [::1].https://merchant.example/payment/success
fail_urlstring · uriΥποχρεωτικόΠροορισμός ανακατεύθυνσης του προγράμματος περιήγησης όταν η πληρωμή αποτυγχάνει, λήγει ή ακυρώνεται. Ισχύουν οι ίδιοι κανόνες με το success_url.https://merchant.example/payment/failure
customerobjectΥποχρεωτικόΤα στοιχεία επικοινωνίας του πληρωτή· χρησιμοποιούνται για το αποτέλεσμα της πληρωμής, την απόδειξη και τα βήματα επαλήθευσης της τράπεζας. Γίνονται δεκτά μόνο τα email και phone· κάθε άλλο πεδίο μέσα στο customer απορρίπτεται με 422 1000001.{ … }
customer.emailstringΥποχρεωτικόΗ πραγματική διεύθυνση e-mail του πληρωτή: RFC 5322, έως 254 χαρακτήρες, με τομέα που περιέχει τελεία. Αποθηκεύεται με πεζά γράμματα.[email protected]
customer.phonestringΥποχρεωτικόΟ πραγματικός αριθμός τηλεφώνου του πληρωτή σε διεθνή μορφή (+ κωδικός χώρας, E.164), για παράδειγμα +37061234567. Κανόνας για την Τουρκία: +90 και ο 10ψήφιος εθνικός αριθμός (5xx κινητό, 2xx, 3xx, 4xx σταθερό, 8xx ειδικοί αριθμοί). Αριθμός που στέλνεται χωρίς κωδικό χώρας αντιμετωπίζεται ως τουρκικός (+90).+37061234567

Τα τρία αυτά URL ισχύουν μόνο για τη συγκεκριμένη πληρωμή και δεν λαμβάνονται από τις ρυθμίσεις του πάνελ σας.

Η σελίδα πληρωμής δεν ζητά ξανά από τον πληρωτή τη διεύθυνση e-mail και τον αριθμό τηλεφώνου. Για λόγους απορρήτου, το customer δεν επιστρέφεται στο αντικείμενο πληρωμής ούτε στα webhooks.

ΠεδίοΑιτίαΜήνυμα στο errors
customer.emailΜη έγκυρη μορφή ή περισσότεροι από 254 χαρακτήρες.customer.email must be a valid e-mail address (max 254 characters).
customer.emailΔιεύθυνση-δείγμα, παράδειγμα ή προσωρινή διεύθυνση.customer.email must be the payer's real address; placeholder, example or disposable addresses (example.com, test@…, mailinator…) are not accepted.
customer.phoneΜη έγκυρος αριθμός σε διεθνή μορφή.customer.phone must be a valid phone number in international format (e.g. +37061234567).
customer.phoneΜοτίβο επαναλαμβανόμενων ή διαδοχικών ψηφίων.customer.phone must be the payer's real number; repeated or sequential digit patterns (5555555555, 1234567890) are not accepted.
customer.phoneΤουρκικός αριθμός που δεν έχει τη μορφή +90 και 10 ψηφία που αρχίζουν από 2, 3, 4, 5 ή 8.customer.phone for Türkiye must be +90 followed by a 10-digit number starting with 2, 3, 4, 5 or 8 (e.g. +905321234567).

HTTP 422 · 1000001

{
  "type": "https://payniko.com/problems/1000001",
  "title": "Validation failed",
  "status": 422,
  "detail": "The submitted fields are not valid.",
  "code": "1000001",
  "correlation_id": "4c7a1e9d-2b3f-4d85-9a6e-0f1b2c3d4e5f",
  "errors": {
    "customer.email": [
      "customer.email must be the payer's real address; placeholder, example or disposable addresses (example.com, test@…, mailinator…) are not accepted."
    ],
    "customer.phone": [
      "customer.phone must be the payer's real number; repeated or sequential digit patterns (5555555555, 1234567890) are not accepted."
    ]
  }
}

Αν ένα αίτημα απορριφθεί λόγω του customer, δείξτε στον πληρωτή την αιτία με σαφήνεια (για παράδειγμα: εισαγάγετε τη δική σας διεύθυνση e-mail ή τον αριθμό τηλεφώνου σας) και αφήστε τον να διορθώσει το πεδίο πριν δημιουργήσετε ξανά την πληρωμή. Τα μηνύματα στο errors είναι στα αγγλικά και προορίζονται για τα αρχεία καταγραφής και την υποστήριξή σας.

Οι σελίδες συνδέσμων πληρωμής της Payniko εφαρμόζουν τους ίδιους κανόνες στη διεύθυνση e-mail και στον αριθμό τηλεφώνου που εισάγει εκεί ο πληρωτής και του δείχνουν την αιτία της απόρριψης.

Σώμα αιτήματος · Παράδειγμα

{
  "merchant_order_id": "ORDER-2026-001",
  "amount": "100.00",
  "currency": "USD",
  "webhook_url": "https://merchant.example/webhooks/payments",
  "success_url": "https://merchant.example/payment/success",
  "fail_url": "https://merchant.example/payment/failure",
  "customer": {
    "email": "[email protected]",
    "phone": "+37061234567"
  }
}
KEY="$(openssl rand -hex 16)"  # store with the order before sending; reuse it after network errors
./payniko.sh POST '/api/payments/hosted' '{
  "merchant_order_id": "ORDER-2026-001",
  "amount": "100.00",
  "currency": "USD",
  "webhook_url": "https://merchant.example/webhooks/payments",
  "success_url": "https://merchant.example/payment/success",
  "fail_url": "https://merchant.example/payment/failure",
  "customer": {
    "email": "[email protected]",
    "phone": "+37061234567"
  }
}' "$KEY"
import {api, randomUUID} from './payniko.mjs';

const key = randomUUID(); // Store this key with the order before sending; reuse it after network errors.
const payment = await api('POST', '/api/payments/hosted', {
  "merchant_order_id": "ORDER-2026-001",
  "amount": "100.00",
  "currency": "USD",
  "webhook_url": "https://merchant.example/webhooks/payments",
  "success_url": "https://merchant.example/payment/success",
  "fail_url": "https://merchant.example/payment/failure",
  "customer": {
    "email": "[email protected]",
    "phone": "+37061234567"
  }
}, key);
console.log(payment);
<?php
require __DIR__ . '/payniko.php';

$key = bin2hex(random_bytes(16)); // store with the order; reuse it after network errors
$payment = payniko_api('POST', '/api/payments/hosted', [
    "merchant_order_id" => 'ORDER-2026-001',
    "amount" => '100.00',
    "currency" => 'USD',
    "webhook_url" => 'https://merchant.example/webhooks/payments',
    "success_url" => 'https://merchant.example/payment/success',
    "fail_url" => 'https://merchant.example/payment/failure',
    "customer" => [
        "email" => '[email protected]',
        "phone" => '+37061234567',
    ],
], $key);
print_r($payment);
from payniko import api
import uuid

key = str(uuid.uuid4())  # store with the order; reuse it after network errors
payment = api("POST", "/api/payments/hosted", {
    "merchant_order_id": "ORDER-2026-001",
    "amount": "100.00",
    "currency": "USD",
    "webhook_url": "https://merchant.example/webhooks/payments",
    "success_url": "https://merchant.example/payment/success",
    "fail_url": "https://merchant.example/payment/failure",
    "customer": {
        "email": "[email protected]",
        "phone": "+37061234567"
    }
}, key)
print(payment)

Απάντηση · 201

Επιστρέφει { "data": Payment } με status: requires_action και next_action.url.

Απάντηση · Παράδειγμα

{
  "data": {
    "payment_id": "LUVEI-PAYN-123456",
    "merchant_order_id": "ORDER-2026-001",
    "flow": "hosted",
    "status": "requires_action",
    "amount": "100.00",
    "currency": "USD",
    "environment": "sandbox",
    "next_action": {
      "type": "redirect",
      "url": "https://payniko.com/l/example-checkout",
      "expires_at": "2026-11-02T12:00:00.000Z"
    },
    "card": null,
    "failure": null,
    "created_at": "2026-11-01T12:00:00.000Z",
    "expires_at": "2026-11-02T12:00:00.000Z",
    "completed_at": null
  }
}

Λοιπές απαντήσεις

  • 200: στάλθηκαν ξανά το ίδιο κλειδί και το ίδιο σώμα. Επιστρέφεται η αρχική πληρωμή και δεν δημιουργείται νέα πληρωμή.
  • 409 9000010: αυτό το κλειδί έχει ήδη χρησιμοποιηθεί με διαφορετικό σώμα.
  • 409 9000039: το πρώτο αίτημα με αυτό το κλειδί βρίσκεται ακόμη υπό επεξεργασία. Στείλτε ξανά το ίδιο αίτημα μετά από σύντομη αναμονή.
  • 409 9000007: υπάρχει ήδη πληρωμή για αυτό το merchant_order_id.
  • 422 9000005: το ποσό βρίσκεται εκτός των ορίων του λογαριασμού σας· το limits περιέχει το επιτρεπόμενο εύρος.
  • 422 1000001 με customer, customer.email ή customer.phone στο errors: τα στοιχεία επικοινωνίας λείπουν ή δεν είναι έγκυρα.
  • 422 1000001, 1000020, 1000021, 9000008, 9000038: ένα πεδίο δεν είναι έγκυρο· λεπτομέρειες περιέχονται στα errors και integration.violations.

HTTP 422 · 9000005

{
  "type": "https://payniko.com/problems/9000005",
  "title": "Invalid amount",
  "status": 422,
  "detail": "The amount is outside the account limits.",
  "code": "9000005",
  "correlation_id": "6d1f0c2e-8a4b-4c7d-9e21-3f5a7b9c1d20",
  "limits": {
    "currency_code": "USD",
    "min_amount": "25.00",
    "max_amount": null
  }
}

Τα όρια ελέγχονται στο κύριο νόμισμα του συστήματος (limits.currency_code), αφού γίνει η μετατροπή του ποσού σας. Το max_amount: null σημαίνει ότι δεν υπάρχει ανώτατο όριο. Οι τιμές ορίων σε αυτό το παράδειγμα είναι ενδεικτικές.

Αντικείμενο πληρωμής

Οι κλήσεις δημιουργίας και ανάκτησης επιστρέφουν αυτό το αντικείμενο μέσα στο data, και κάθε ειδοποίηση webhook πληρωμής το περιέχει. Η δομή του είναι ίδια στο Hosted API και στο Direct API.

ΠεδίοΤύποςΠεριγραφή
payment_idstring · LUVEI-ABCD-123456Το αναγνωριστικό της πληρωμής στο Payniko . Αποθηκεύστε το μαζί με την παραγγελία σας.
merchant_order_idstringΟ κωδικός της παραγγελίας σας, ακριβώς όπως υποβλήθηκε.
flowstringhosted για το Hosted API, direct για το Direct API.
hosteddirect
statusstringΗ τρέχουσα κατάσταση της πληρωμής· δείτε τον πίνακα καταστάσεων παρακάτω.
requires_actionprocessingsucceededfailedexpiredcancelled
amountstringΠοσό με δύο δεκαδικά ψηφία.
currencystringΤο νόμισμα του ποσού· το κύριο νόμισμα του συστήματος, αν δεν ορίστηκε νόμισμα.
environmentstringΤο περιβάλλον του κλειδιού με το οποίο δημιουργήθηκε η πληρωμή.
sandboxlive
next_actionobject | nullΗ επόμενη ενέργεια που απαιτείται από τον πληρωτή. Ορίζεται μόνο όσο το status είναι requires_action.
next_action.typestringΠάντα redirect.
next_action.urlstring · uriΑνοίξτε αυτό το URL στο πρόγραμμα περιήγησης του πληρωτή ακριβώς όπως επιστρέφεται. Μην το κατασκευάζετε και μην το τροποποιείτε ποτέ.
next_action.expires_atstring · date-timeΠροθεσμία χρήσης του URL. Στο Direct API, αντιστοιχεί στο τέλος του βήματος 3-D Secure και μπορεί να είναι νωρίτερα από το expires_at της ίδιας της πληρωμής· στο Hosted API, είναι η προθεσμία της πληρωμής. Μόλις παρέλθει, η πληρωμή μεταβαίνει στην επόμενη κατάστασή της και το next_action γίνεται null.
cardobject | nullΣτοιχεία κάρτας με απόκρυψη, μόλις γίνουν γνωστά.
card.bin6string | nullΤα πρώτα έξι ψηφία.
card.last_fourstring | nullΤα τελευταία τέσσερα ψηφία.
card.networkstring | nullΤο δίκτυο της κάρτας.
visamastercardamextroyunionpaydiscoverjcbdinersmaestro
card.bankstring | nullΗ εκδότρια τράπεζα, αν είναι γνωστή.
failureobject | nullΗ αιτία της αποτυχίας. Ορίζεται μόνο όταν το status είναι failed.
failure.codestringΚωδικός αιτίας για χρήση στη λογική της εφαρμογής σας· δείτε τη λίστα παρακάτω.
failure.messagestringΣύντομο μήνυμα κατάλληλο για εμφάνιση στον πληρωτή ως απλό κείμενο.
created_atstring · date-timeΧρόνος δημιουργίας της πληρωμής.
expires_atstring · date-timeΠροθεσμία ολοκλήρωσης της πληρωμής.
completed_atstring · date-time | nullΧρόνος κατά τον οποίο η πληρωμή περιήλθε σε τελική κατάσταση.

Το next_action μπορεί για σύντομο διάστημα να είναι null ακόμη και σε requires_action· σε αυτή την περίπτωση, ανακτήστε ξανά την πληρωμή. Ο πλήρης αριθμός κάρτας, το CVV, η ημερομηνία λήξης και το όνομα του κατόχου δεν επιστρέφονται ποτέ. Τα card.network και card.bank μπορεί να είναι null όταν δεν είναι γνωστά.

Τα στοιχεία customer που στάλθηκαν κατά τη δημιουργία της πληρωμής δεν αποτελούν μέρος αυτού του αντικειμένου και δεν εμφανίζονται στα webhooks.

Πληρωμή · Παράδειγμα

{
  "data": {
    "payment_id": "LUVEI-PAYN-123456",
    "merchant_order_id": "ORDER-2026-001",
    "flow": "hosted",
    "status": "requires_action",
    "amount": "100.00",
    "currency": "USD",
    "environment": "sandbox",
    "next_action": {
      "type": "redirect",
      "url": "https://payniko.com/l/example-checkout",
      "expires_at": "2026-11-02T12:00:00.000Z"
    },
    "card": null,
    "failure": null,
    "created_at": "2026-11-01T12:00:00.000Z",
    "expires_at": "2026-11-02T12:00:00.000Z",
    "completed_at": null
  }
}

Καταστάσεις πληρωμής Hosted API

ΚατάστασηΣημασίαΣυνιστώμενη ενέργεια
requires_actionΗ πληρωμή έχει δημιουργηθεί και ο πληρωτής δεν την έχει ολοκληρώσει ακόμη.Ανακατευθύνετε τον πληρωτή στο next_action.url (έως το next_action.expires_at).
processingΟ πληρωτής έχει πληρώσει και η απάντηση της τράπεζας εκκρεμεί.Περιμένετε το webhook και μη δημιουργήσετε νέα πληρωμή. Αν η τράπεζα δεν απαντήσει ποτέ, η πληρωμή μπορεί να παραμείνει σε processing μέχρι τη συμφωνία και στη συνέχεια καταλήγει σε failed ή expired.
succeededΤα κεφάλαια έχουν εισπραχθεί.Εκτελέστε την παραγγελία.
failedΗ πληρωμή δεν ολοκληρώθηκε. Το failure περιέχει την αιτία.Εμφανίστε το failure.message. Για νέα προσπάθεια, δημιουργήστε νέα πληρωμή με νέο merchant_order_id.
expiredΟ πληρωτής δεν ολοκλήρωσε την πληρωμή εντός 24 ωρών (expires_at). Αν ο πληρωτής εγκαταλείψει την πληρωμή στο μέσο της διαδικασίας, μπορεί αντί αυτού να καταλήξει σε failed.Αν ο πληρωτής εξακολουθεί να επιθυμεί να πληρώσει, δημιουργήστε νέα πληρωμή.
cancelledΗ πληρωμή ακυρώθηκε.Μην εκτελέσετε την παραγγελία.
  • Μια ενδιάμεση απόρριψη στη σελίδα πληρωμής δεν είναι οριστική: η πληρωμή μπορεί να παραμείνει σε requires_action και ενδέχεται να λάβετε payment.updated. Τελικά λαμβάνετε ένα οριστικό αποτέλεσμα.
  • Οι καταστάσεις succeeded, failed, expired και cancelled είναι τελικές. Μια πληρωμή σε processing μπορεί να ολοκληρωθεί επιτυχώς ακόμη και μετά το expires_at.
  • Αντιμετωπίστε κάθε κατάσταση που δεν αναφέρεται εδώ ως processing και ανακτήστε ξανά την πληρωμή.

Κωδικοί αποτυχίας

Μια πληρωμή σε failed περιέχει τα failure.code και failure.message. Χρησιμοποιήστε τον κωδικό στη λογική της εφαρμογής σας και εμφανίστε το μήνυμα στον πληρωτή ως απλό κείμενο.

ΚωδικόςΣημασία
payment_declinedΗ τράπεζα απέρριψε την πληρωμή.
do_not_honorΗ τράπεζα απέρριψε την πληρωμή χωρίς να αναφέρει αιτιολογία.
insufficient_fundsΗ κάρτα δεν διαθέτει επαρκή κεφάλαια.
card_limit_reachedΈχει συμπληρωθεί ένα όριο της κάρτας.
card_details_invalidΤα στοιχεία της κάρτας δεν είναι έγκυρα.
card_expiry_invalidΗ ημερομηνία λήξης δεν είναι έγκυρη ή η κάρτα έχει λήξει.
card_security_code_invalidΤο CVV δεν είναι έγκυρο.
card_not_activeΗ κάρτα είναι αποκλεισμένη ή ανενεργή.
card_brand_not_supportedΑυτό το δίκτυο καρτών δεν υποστηρίζεται.
three_ds_failedΟ έλεγχος ταυτότητας 3-D Secure απέτυχε ή δεν ολοκληρώθηκε.
payment_not_permittedΗ κάρτα δεν επιτρέπεται για αυτό το είδος πληρωμής.
amount_not_allowedΤο ποσό δεν επιτρέπεται για αυτή την κάρτα.
currency_not_supportedΤο νόμισμα δεν υποστηρίζεται για αυτή την κάρτα.
payment_details_rejectedΗ τράπεζα απέρριψε τα στοιχεία της πληρωμής.
duplicate_paymentΗ τράπεζα αναγνώρισε την πληρωμή ως διπλότυπη.
payment_timed_outΗ πληρωμή δεν ολοκληρώθηκε εγκαίρως.
bank_unavailableΔεν ήταν δυνατή η επικοινωνία με την τράπεζα.
bank_requested_retryΗ τράπεζα ζήτησε επανάληψη της προσπάθειας σε μεταγενέστερο χρόνο.
service_unavailableΔεν ήταν δυνατή η επεξεργασία της πληρωμής αυτή τη στιγμή.

Ενδέχεται να προστεθούν νέοι κωδικοί· χειριστείτε κάθε άγνωστο κωδικό ως payment_declined. Το failure.message μπορεί να περιέχει το κείμενο της ίδιας της τράπεζας· διαφορετικά, είναι στα αγγλικά στις ειδοποιήσεις webhook και ακολουθεί το Accept-Language στο GET.

Απαιτήσεις ανακατεύθυνσης

  • Ανακατευθύνετε ολόκληρη τη σελίδα (303 ή window.location) στο next_action.url ή ανοίξτε το σε νέα καρτέλα. Μην το ενσωματώνετε σε iframe· η σελίδα αποκλείει τη φόρτωση σε πλαίσιο.
  • Χρησιμοποιήστε το URL πριν από το next_action.expires_at. Μετά τη λήξη του, ανακτήστε την πληρωμή.
  • Η γλώσσα της σελίδας ακολουθεί τις ρυθμίσεις του προγράμματος περιήγησης του πληρωτή· δεν απαιτείται καμία παράμετρος.

URL επιστροφής

Κατά την επιστροφή του πληρωτή, το πρόγραμμα περιήγησης ανακατευθύνεται στο success_url μόνο αν η πληρωμή είναι succeeded και στο fail_url σε κάθε άλλη περίπτωση. Στο URL προστίθενται υπογεγραμμένες παράμετροι. Επαληθεύστε τις για να εμφανίσετε τη σωστή σελίδα και εκτελέστε την παραγγελία μόνο αφού η ειδοποίηση webhook ή ένα GET επιβεβαιώσει την κατάσταση succeeded.

ΠεδίοΠεριγραφή
payment_idΤο αναγνωριστικό της πληρωμής .
merchant_order_idΟ κωδικός της παραγγελίας σας. Δεν υπογράφεται: χρησιμοποιήστε τον μόνο για τον εντοπισμό της παραγγελίας και κάντε την αντιστοίχιση με βάση το payment_id.
statusΚατάσταση της πληρωμής τη στιγμή της ανακατεύθυνσης, με τις ίδιες τιμές όπως στο GET. Πάντα succeeded στο success_url. Στο fail_url, συνήθως failed, expired ή cancelled· με το Hosted API μπορεί να είναι requires_action ή processing αν ο πληρωτής έφυγε πριν η πληρωμή φτάσει σε τελική κατάσταση, για παράδειγμα μέσω του "Επιστροφή στο κατάστημα".
failure_codeΥπάρχει μόνο όταν το status είναι failed· ταυτίζεται με το failure.code.
tsΏρα Unix δημιουργίας του συνδέσμου επιστροφής. Ο πληρωτής μπορεί να ανοίξει τον σύνδεσμο αργότερα, επομένως ένα παλαιό ts από μόνο του δεν υποδηλώνει πλαστογράφηση.
key_versionΠροσδιορίζει το κλειδί που υπέγραψε τον σύνδεσμο: 1 για το πρώτο κλειδί που δημιουργήθηκε στο συγκεκριμένο περιβάλλον, 2 για το επόμενο κ.ο.κ. Η τιμή δεν αλλάζει ποτέ για μια δεδομένη πληρωμή.
sigHMAC-SHA256 σε δεκαεξαδική μορφή με πεζά, 64 χαρακτήρες.

Συμβολοσειρά προς υπογραφή

ts + "." + payment_id + "." + status + "." + failure_code   // failure_code empty: keep the last dot
  1. Επιλέξτε το μυστικό κλειδί που αντιστοιχεί στο κλειδί το οποίο προσδιορίζει το key_version. Πρόκειται για το κλειδί με το οποίο δημιουργήθηκε η πληρωμή, ακόμη και αν τα κλειδιά έχουν αντικατασταθεί στο μεταξύ.

  2. Συνθέστε τη συμβολοσειρά από τις τιμές μετά την αποκωδικοποίηση URL. Αν δεν υπάρχει failure_code, διατηρήστε την τελική τελεία.

  3. Υπολογίστε το HMAC-SHA256 και συγκρίνετέ το με το sig σε σταθερό χρόνο. Βεβαιωθείτε ότι το payment_id αντιστοιχεί σε μία από τις πληρωμές σας.

  4. Ανακτήστε την πληρωμή με GET /api/payments/{payment_id} (ή περιμένετε την ειδοποίηση webhook) πριν εκτελέσετε την παραγγελία.

  • Οι δικές σας παράμετροι ερωτήματος διατηρούνται. Αν το URL περιέχει ήδη παράμετρο με ένα από τα παραπάνω ονόματα, αυτή αντικαθίσταται.
  • Αν ο σύνδεσμος δεν ήταν δυνατό να υπογραφεί λόγω προσωρινού σφάλματος, το URL δεν περιέχει καμία από αυτές τις παραμέτρους. Θεωρήστε το αποτέλεσμα άγνωστο και ανακτήστε την πληρωμή.
// Node.js 20+: check the signed parameters on success_url / fail_url.
import {createHmac, timingSafeEqual} from 'node:crypto';

// query: URLSearchParams of the return URL.
// secretFor(keyVersion): the API secret of that key, or undefined if you do not know it.
export function verifyReturn(query, secretFor) {
  const sig = query.get('sig') ?? '';
  const secret = secretFor(query.get('key_version'));
  if (!secret || !/^[0-9a-f]{64}$/.test(sig)) return false;
  const signed = [query.get('ts') ?? '', query.get('payment_id') ?? '',
    query.get('status') ?? '', query.get('failure_code') ?? ''].join('.');
  const expected = createHmac('sha256', secret).update(signed, 'utf8').digest();
  return timingSafeEqual(expected, Buffer.from(sig, 'hex'));
}
// Even a valid return is not proof of payment: read GET /api/payments/{payment_id} before you deliver.
<?php
// PHP 8.1+: check the signed parameters on success_url / fail_url.
// $secretFor(string $keyVersion): the API secret of that key, or null if you do not know it.
function payniko_verify_return(array $query, callable $secretFor): bool
{
    $sig = (string) ($query['sig'] ?? '');
    $secret = $secretFor((string) ($query['key_version'] ?? ''));
    if (!$secret || !preg_match('/^[0-9a-f]{64}$/', $sig)) return false;
    $signed = implode('.', [(string) ($query['ts'] ?? ''), (string) ($query['payment_id'] ?? ''),
        (string) ($query['status'] ?? ''), (string) ($query['failure_code'] ?? '')]);
    return hash_equals(hash_hmac('sha256', $signed, $secret), $sig);
}

// $ok = payniko_verify_return($_GET, fn (string $v) => $secretsByKeyVersion[$v] ?? null);
// Even a valid return is not proof of payment: read GET /api/payments/{payment_id} before you deliver.
# Python 3.9+: check the signed parameters on success_url / fail_url.
import hashlib, hmac, re


def verify_return(query, secret_for) -> bool:
    # query: dict-like (for example Flask request.args); secret_for(key_version) -> secret or None
    sig = query.get("sig", "")
    secret = secret_for(query.get("key_version"))
    if not secret or not re.fullmatch(r"[0-9a-f]{64}", sig):
        return False
    signed = ".".join([query.get("ts", ""), query.get("payment_id", ""),
                       query.get("status", ""), query.get("failure_code", "")])
    expected = hmac.new(secret.encode(), signed.encode(), hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, sig)

# Even a valid return is not proof of payment: read GET /api/payments/{payment_id} before you deliver.

URL επιστροφής · Παράδειγμα

https://merchant.example/payment/success?payment_id=LUVEI-PAYN-654321&merchant_order_id=ORDER-2026-001&status=succeeded&ts=1793001234&key_version=1&sig=200e8a5d4a7a03ead362a2c076408f9d8764f8f636f58bd4c17d78a50d59eb8a
string to sign: 1793001234.LUVEI-PAYN-654321.succeeded.

https://merchant.example/payment/failure?payment_id=LUVEI-PAYN-654321&merchant_order_id=ORDER-2026-001&status=failed&failure_code=insufficient_funds&ts=1793001234&key_version=1&sig=1cd0a104ec4ec4f5b2933d48d14c0eb33d949dd7f27936a067ab8f68b413b70d
string to sign: 1793001234.LUVEI-PAYN-654321.failed.insufficient_funds

secret used in this example (fake): example_secret_not_real
GET/api/payments/{payment_id}

Ανάκτηση πληρωμής

Επιστρέφει { "data": Payment } με την τρέχουσα κατάσταση. Η ανάκτηση δεν τροποποιεί ποτέ την πληρωμή, επομένως μπορείτε να καλείτε αυτό το endpoint όσο συχνά απαιτείται: μετά την επιστροφή του πληρωτή, όταν μια ειδοποίηση webhook φαίνεται ασυνεπής ή μετά από λήξη χρονικού ορίου.

Παράμετροι

ΠεδίοΤύποςΥποχρεωτικόΠεριγραφή
payment_idstring · pathΥποχρεωτικόΤο payment_id που επιστράφηκε κατά τη δημιουργία της πληρωμής. Οποιοδήποτε κλειδί του ίδιου περιβάλλοντος μπορεί να την ανακτήσει.
./payniko.sh GET '/api/payments/LUVEI-PAYN-123456'
import {api} from './payniko.mjs';

const payment = await api('GET', '/api/payments/LUVEI-PAYN-123456');
console.log(payment);
<?php
require __DIR__ . '/payniko.php';

$payment = payniko_api('GET', '/api/payments/LUVEI-PAYN-123456');
print_r($payment);
from payniko import api

payment = api("GET", "/api/payments/LUVEI-PAYN-123456")
print(payment)

Πιθανά σφάλματα

Ειδοποιήσεις webhook πληρωμής

Τα παρακάτω συμβάντα αποστέλλονται στο webhook_url της πληρωμής. Κάθε συμβάν πληρωμής αποστέλλεται το πολύ μία φορά ανά πληρωμή.

ΣυμβάνΣημασία
payment.succeededΗ κατάσταση της πληρωμής είναι succeeded. Εκτελέστε την παραγγελία.
payment.failedΗ πληρωμή ολοκληρώθηκε ανεπιτυχώς: failed, expired ή cancelled. Δείτε το data.status.
payment.updatedΑπαιτείται ακόμη ενέργεια από τον πληρωτή (requires_action), για παράδειγμα μετά από ενδιάμεση απόρριψη. Δεν πρόκειται για τελικό αποτέλεσμα.
refund.* · payment.voided · dispute.*Μεταγενέστερα συμβάντα για αυτή την πληρωμή (επιστροφές χρημάτων, ακυρώσεις, αμφισβητήσεις). Δείτε Ειδοποιήσεις webhook.

payment.succeeded

{
  "event_id": "e0743f4c-a297-4bf0-8085-ceb7d9d5e181",
  "event_type": "payment.succeeded",
  "environment": "sandbox",
  "occurred_at": "2026-11-01T12:02:00.000Z",
  "data": {
    "payment_id": "LUVEI-PAYN-123456",
    "merchant_order_id": "ORDER-2026-001",
    "flow": "hosted",
    "status": "succeeded",
    "amount": "100.00",
    "currency": "USD",
    "environment": "sandbox",
    "next_action": null,
    "card": {
      "bin6": "400000",
      "last_four": "0002",
      "network": "visa",
      "bank": null
    },
    "failure": null,
    "created_at": "2026-11-01T12:00:00.000Z",
    "expires_at": "2026-11-02T12:00:00.000Z",
    "completed_at": "2026-11-01T12:02:00.000Z"
  }
}

Επαληθεύετε κάθε ειδοποίηση webhook: το HMAC-SHA256 του X-Timestamp + "." + raw body, υπολογισμένο με το μυστικό κλειδί που αντιστοιχεί στο κλειδί API με το οποίο δημιουργήθηκε η πληρωμή, πρέπει να ισούται με το X-Signature και το X-Timestamp πρέπει να βρίσκεται εντός 300 δευτερολέπτων. Αποθηκεύστε κάθε event_id μία φορά και απαντήστε άμεσα με 2xx. Για παραδείγματα κώδικα και τη συμπεριφορά των επαναλήψεων, δείτε Ειδοποιήσεις webhook.

Σφάλματα

ΚωδικόςHTTPΣημασίαΣυνιστώμενη ενέργεια
1000001422Το αίτημα δεν είναι έγκυρο: άγνωστο πεδίο, μη έγκυρη μορφή, συμβολοσειρά ερωτήματος σε POST, παράμετρος ερωτήματος που δεν υποστηρίζεται ή σώμα που δεν είναι αντικείμενο JSON. Το errors παραθέτει τα πεδία που αφορά το σφάλμα.Διορθώστε τα πεδία που αναφέρονται.
1000009422Η κεφαλίδα Idempotency-Key λείπει ή δεν είναι έγκυρη.Σε κάθε POST στείλτε κλειδί 8–128 χαρακτήρων από το σύνολο A-Z a-z 0-9 _ -.
9000010409Αυτό το Idempotency-Key έχει ήδη χρησιμοποιηθεί για διαφορετικό αίτημα ή το ίδιο υπογεγραμμένο αίτημα στάλθηκε ξανά με διαφορετικό κλειδί.Επαναλάβετε το αίτημα με το ίδιο κλειδί και το ίδιο σώμα· χρησιμοποιήστε νέο κλειδί μόνο για νέα λειτουργία.
9000039409Το πρώτο αίτημα με αυτό το Idempotency-Key βρίσκεται ακόμη υπό επεξεργασία (Hosted API).Στείλτε ξανά το ίδιο αίτημα μετά από σύντομη αναμονή.
9000007409Υπάρχει ήδη πληρωμή για αυτό το merchant_order_id (Hosted API).Ανακτήστε την υπάρχουσα πληρωμή ή χρησιμοποιήστε νέο κωδικό παραγγελίας.
9000005422Το ποσό βρίσκεται εκτός των ορίων του λογαριασμού σας (Hosted API). Το limits περιέχει το επιτρεπόμενο εύρος.Προσαρμόστε το ποσό.
1000020422Η μορφή του ποσού δεν είναι έγκυρη.Στείλτε θετικό ποσό ως συμβολοσειρά, με δύο δεκαδικά ψηφία και χωρίς αρχικά μηδενικά, για παράδειγμα "100.00".
1000021422Το νόμισμα δεν υποστηρίζεται.Χρησιμοποιήστε USD, EUR, AED ή TRY ή παραλείψτε το currency.
9000008422Το webhook_url δεν είναι έγκυρο.Χρησιμοποιήστε δημόσιο URL HTTPS στη θύρα 443 με μέγιστο μήκος 500 χαρακτήρων.
9000038422Το success_url ή το fail_url δεν είναι έγκυρο.Χρησιμοποιήστε URL HTTPS με μέγιστο μήκος 2048 χαρακτήρων. Το sandbox δέχεται επίσης http://localhost.
9000040403Το Hosted API δεν είναι ενεργοποιημένο για τον λογαριασμό σας.Επικοινωνήστε με την υποστήριξη.
9000013409Η λειτουργία sandbox δεν είναι διαθέσιμη, για παράδειγμα όταν ένα endpoint αποκλειστικά για sandbox καλείται με κλειδί παραγωγής.Χρησιμοποιήστε κλειδί sandbox ή επαναλάβετε το αίτημα αργότερα.
9000004403Ο λογαριασμός σας έχει περιορισμό για αυτή τη λειτουργία.Επικοινωνήστε με την υποστήριξη.
3000001503Η συναλλαγματική ισοτιμία δεν είναι προσωρινά διαθέσιμη.Επαναλάβετε το αίτημα αργότερα.
1000011503Η υπηρεσία δεν είναι προσωρινά διαθέσιμη.Επαναλάβετε το ίδιο αίτημα. Ανακτήστε τους υπάρχοντες πόρους πριν δημιουργήσετε νέους.
1000013503Αυτή η λειτουργία δεν είναι ακόμη διαθέσιμη.Επαναλάβετε το αίτημα μετά από retry_after δευτερόλεπτα.
1000014503Βρίσκεται σε εξέλιξη προγραμματισμένη συντήρηση. Λεπτομέρειες παρέχουν τα until και note.Επαναλάβετε το αίτημα μετά το διάστημα που ορίζει το Retry-After.

Η απόρριψη κάρτας δεν αποτελεί σφάλμα HTTP· η πληρωμή καταλήγει σε failed με failure.code. Για τον πλήρη κατάλογο, δείτε Κωδικοί σφαλμάτων.

Δοκιμές με κάρτες sandbox

Δημιουργήστε την πληρωμή με το κλειδί sandbox, ανοίξτε το next_action.url και πληρώστε με μία από τις παρακάτω κάρτες, με οποιαδήποτε μελλοντική ημερομηνία λήξης και οποιοδήποτε τριψήφιο CVV. Για λεπτομέρειες, δείτε Sandbox και δοκιμαστικές κάρτες.

Αριθμός κάρταςΑποτέλεσμα
4000000000000010Εγκρίνεται χωρίς 3-D Secure. Στο Direct API, η απάντηση δημιουργίας επιστρέφει ήδη succeeded.
4000000000000002 · 4355090000044242Δοκιμαστικό βήμα 3-D Secure και στη συνέχεια succeeded.
4000000000000051 · 4355090000040000Απορρίπτεται πριν από το 3-D Secure (do_not_honor). Hosted API: η πληρωμή μπορεί να παραμείνει σε requires_action για περίπου 5 λεπτά και να στείλει payment.updated πριν καταλήξει σε failed. Direct API: η απάντηση δημιουργίας επιστρέφει ήδη failed.
4000000000000093 · 4355090000005151Δοκιμαστικό βήμα 3-D Secure και στη συνέχεια failed με insufficient_funds.
4000000000000044Χωρίς απάντηση από την τράπεζα και χωρίς 3-D Secure· η πληρωμή παραμένει σε processing.
4355090000089999Hosted API: processing χωρίς βήμα 3-D Secure. Direct API: δοκιμαστικό βήμα 3-D Secure και στη συνέχεια processing.

Ολοκληρωμένο παράδειγμα

Ένας ελάχιστος διακομιστής Node.js που δημιουργεί πληρωμή, ανακατευθύνει τον πληρωτή και χειρίζεται την επιστροφή και την ειδοποίηση webhook. Χρησιμοποιεί τη βοηθητική συνάρτηση αιτημάτων και τις συναρτήσεις επαλήθευσης από τις ενότητες Ειδοποιήσεις webhook και URL επιστροφής.

server.mjs

// server.mjs: Hosted API from start to end (Node.js 20+, no dependencies).
import http from 'node:http';
import {randomUUID} from 'node:crypto';
import {api} from './payniko.mjs';                  // request helper (Authentication)
import {verifyWebhook} from './verify-webhook.mjs'; // webhook check (Webhooks)
import {verifyReturn} from './verify-return.mjs';   // return URL check (Return URL)

const site = 'https://merchant.example';
const orders = new Map();      // use your database
const seenEvents = new Set();  // use your database
const readBody = async (req) => { const parts = []; for await (const p of req) parts.push(p); return Buffer.concat(parts); };

http.createServer(async (req, res) => {
  const url = new URL(req.url, site);
  // 1. Create the payment, 2. redirect the payer.
  if (req.method === 'POST' && url.pathname === '/checkout') {
    const form = JSON.parse((await readBody(req)).toString('utf8')); // your checkout form: the payer's e-mail and phone
    const orderId = 'ORDER-' + Date.now();
    const key = randomUUID();
    orders.set(orderId, {key, status: 'new'});      // save the key before you call the API
    const {data} = await api('POST', '/api/payments/hosted', {
      merchant_order_id: orderId, amount: '100.00', currency: 'USD',
      customer: {email: form.email, phone: form.phone},
      webhook_url: `${site}/webhooks/payniko`,
      success_url: `${site}/payment/success`, fail_url: `${site}/payment/failure`,
    }, key);
    Object.assign(orders.get(orderId), {paymentId: data.payment_id, status: data.status});
    // next_action can be null for a moment: read the payment again before you redirect.
    res.writeHead(303, {Location: data.next_action ? data.next_action.url : `/orders/${orderId}`}).end();
    return;
  }
  // Return URL: show a page, but trust only the webhook or a GET.
  if (req.method === 'GET' && (url.pathname === '/payment/success' || url.pathname === '/payment/failure')) {
    const signed = verifyReturn(url.searchParams, () => process.env.API_SECRET);
    let status = 'unknown';
    if (signed) status = (await api('GET', `/api/payments/${url.searchParams.get('payment_id')}`)).data.status;
    res.writeHead(200, {'Content-Type': 'text/plain; charset=utf-8'});
    res.end(status === 'succeeded' ? 'Thank you, payment received.' : 'Payment not completed.');
    return;
  }
  // Webhook: verify, store event_id once, answer 2xx fast.
  if (req.method === 'POST' && url.pathname === '/webhooks/payniko') {
    const raw = await readBody(req);
    if (!verifyWebhook(raw, req.headers, process.env.API_SECRET)) { res.writeHead(401).end(); return; }
    const event = JSON.parse(raw.toString('utf8'));
    if (!seenEvents.has(event.event_id)) {          // use a unique index in your database
      seenEvents.add(event.event_id);
      const order = orders.get(event.data.merchant_order_id);
      if (order && event.event_type.startsWith('payment.')) order.status = event.data.status;
    }
    res.writeHead(204).end();
    return;
  }
  res.writeHead(404).end();
}).listen(8080);

Ειδοποιήσεις webhook

Η Payniko στέλνει ένα υπογεγραμμένο POST στο webhook_url σας όταν αλλάζει η κατάσταση μιας πληρωμής, επιστροφής χρημάτων, αμφισβήτησης ή ανάληψης· η πρώτη προσπάθεια γίνεται συνήθως μέσα σε ένα λεπτό από το συμβάν. Επαληθεύστε την υπογραφή, αποθηκεύστε το συμβάν και απαντήστε με 2xx πριν το επεξεργαστείτε.

Συμβάντα

ΣυμβάνΣημασία
payment.succeededΗ κατάσταση της πληρωμής είναι succeeded. Εκτελέστε την παραγγελία.
payment.failedΗ πληρωμή ολοκληρώθηκε ανεπιτυχώς: failed, expired ή cancelled. Δείτε το data.status.
payment.updatedΑπαιτείται ακόμη ενέργεια από τον πληρωτή (requires_action), για παράδειγμα μετά από ενδιάμεση απόρριψη. Δεν πρόκειται για τελικό αποτέλεσμα.
refund.completedΜια επιστροφή χρημάτων ολοκληρώθηκε.
payment.voidedΜια ακύρωση ολοκληρώθηκε.
refund.rejectedΜια επιστροφή χρημάτων ή ακύρωση απορρίφθηκε.
refund.failedΜια επιστροφή χρημάτων ή ακύρωση απέτυχε.
refund.cancelledΈνα αίτημα επιστροφής χρημάτων ή ακύρωσης ακυρώθηκε.
dispute.openedΆνοιξε μια αμφισβήτηση.
dispute.in_defenseΗ αμφισβήτηση βρίσκεται υπό εξέταση.
dispute.wonΗ αμφισβήτηση κρίθηκε υπέρ σας.
dispute.lostΗ αμφισβήτηση κρίθηκε εις βάρος σας.
withdrawal.sentΜια ανάληψη αποστάλθηκε.
withdrawal.rejectedΜια ανάληψη απορρίφθηκε.

Τα συμβάντα πληρωμών, επιστροφών χρημάτων και αμφισβητήσεων αποστέλλονται στο webhook_url της πληρωμής, ενώ τα συμβάντα αναλήψεων στο webhook_url της ανάληψης. Κάθε συμβάν πληρωμής αποστέλλεται το πολύ μία φορά ανά πληρωμή· κάθε συμβάν επιστροφής χρημάτων, αμφισβήτησης ή ανάληψης το πολύ μία φορά ανά επιστροφή χρημάτων, αμφισβήτηση ή ανάληψη και ανά κατάσταση.

Περιεχόμενο ειδοποίησης

ΠεδίοΤύποςΠεριγραφή
event_idstring · uuidΜοναδικό αναγνωριστικό του συμβάντος· ταυτίζεται με την κεφαλίδα X-Event-Id.
event_typestringΤο όνομα του συμβάντος, όπως αναφέρεται στον παραπάνω πίνακα.
environmentstringsandbox ή live.
occurred_atstring · date-timeΧρόνος πραγματοποίησης του συμβάντος.
subjectobjectΥπάρχει μόνο σε συμβάντα επιστροφών χρημάτων, αμφισβητήσεων και αναλήψεων: τα type και id της επιστροφής χρημάτων, της αμφισβήτησης ή της ανάληψης.
dataobjectΓια συμβάντα πληρωμών, το αντικείμενο πληρωμής (δείτε Hosted API)· για τα λοιπά συμβάντα, τα πεδία που αναφέρονται παρακάτω.

payment.succeeded

{
  "event_id": "e0743f4c-a297-4bf0-8085-ceb7d9d5e181",
  "event_type": "payment.succeeded",
  "environment": "sandbox",
  "occurred_at": "2026-11-01T12:02:00.000Z",
  "data": {
    "payment_id": "LUVEI-PAYN-123456",
    "merchant_order_id": "ORDER-2026-001",
    "flow": "hosted",
    "status": "succeeded",
    "amount": "100.00",
    "currency": "USD",
    "environment": "sandbox",
    "next_action": null,
    "card": {
      "bin6": "400000",
      "last_four": "0002",
      "network": "visa",
      "bank": null
    },
    "failure": null,
    "created_at": "2026-11-01T12:00:00.000Z",
    "expires_at": "2026-11-02T12:00:00.000Z",
    "completed_at": "2026-11-01T12:02:00.000Z"
  }
}

Συμβάντα επιστροφών χρημάτων, αμφισβητήσεων και αναλήψεων

ΟντότηταΠεδίο
refundid, status, operation_type, amount, currency, payment_id ή payment_code, merchant_order_id
disputeid, status, amount, currency, payment_id ή payment_code, merchant_order_id, reason, evidence_due_at (εφόσον έχει οριστεί)
withdrawalid, status, amount, currency, usdt_amount, asset, network, tx_hash
  • Τα συμβάντα επιστροφών χρημάτων και αμφισβητήσεων περιέχουν μία μόνο αναφορά πληρωμής: payment_id ή, σε ορισμένα συμβάντα, payment_code. Το refund.completed περιλαμβάνει πάντα payment_id. Όταν ένα συμβάν περιέχει payment_code, αντιστοιχίστε την παραγγελία με βάση το merchant_order_id.
  • Πριν ενεργήσετε βάσει ενός συμβάντος, ανακτήστε την επιστροφή χρημάτων, την αμφισβήτηση ή την ανάληψη με GET για να επιβεβαιώσετε την τρέχουσα κατάστασή της.

refund.completed

{
  "event_id": "c4b2e7d1-8a3f-4e65-b0d9-5f1a2c3e4d70",
  "event_type": "refund.completed",
  "environment": "sandbox",
  "occurred_at": "2026-11-02T09:20:00.000Z",
  "subject": {
    "type": "refund",
    "id": "LUVEI-RFND-100001"
  },
  "data": {
    "id": "LUVEI-RFND-100001",
    "status": "completed",
    "operation_type": "refund",
    "amount": "100.00",
    "currency": "USD",
    "payment_id": "LUVEI-PAYN-123456",
    "merchant_order_id": "ORDER-2026-001"
  }
}

withdrawal.sent

{
  "event_id": "b3e8f0a2-5c71-4d96-8e14-7a2c9d0f6b35",
  "event_type": "withdrawal.sent",
  "environment": "sandbox",
  "occurred_at": "2026-11-02T10:00:00.000Z",
  "subject": {
    "type": "withdrawal",
    "id": "LUVEI-WTHD-100002"
  },
  "data": {
    "id": "LUVEI-WTHD-100002",
    "status": "sent",
    "amount": "50.00",
    "currency": "USD",
    "usdt_amount": "49.950000",
    "asset": "USDT",
    "network": "TRC20",
    "tx_hash": "9f2b6c1d4e8a7f30b5c2d9e1a6f4b7c08d3e5a2f1b9c6d4e7a0f3b8c5d2e1f6a"
  }
}

Επαλήθευση υπογραφής

ΚεφαλίδαΤιμή
X-Event-IdΤαυτίζεται με το event_id.
X-Environmentsandbox ή live. Χρησιμοποιήστε το μυστικό κλειδί του αντίστοιχου περιβάλλοντος.
X-TimestampΏρα Unix αυτής της προσπάθειας αποστολής.
X-SignatureHMAC-SHA256 του X-Timestamp + "." + raw body σε δεκαεξαδική μορφή με πεζά.
  1. Διαβάστε τα byte του ακατέργαστου σώματος πριν αναλύσετε το JSON.

  2. Επαληθεύστε ότι το X-Timestamp έχει 10 ψηφία και απέχει έως 300 δευτερόλεπτα από την ώρα του διακομιστή σας.

  3. Υπολογίστε το HMAC-SHA256 του X-Timestamp + "." + raw body με το μυστικό κλειδί API σας και συγκρίνετε το αποτέλεσμα με το X-Signature σε σταθερό χρόνο.

  4. Αν η επαλήθευση αποτύχει, απορρίψτε το αίτημα με 401.

// Node.js 20+: verify a Payniko notification before parsing it.
import {createHmac, timingSafeEqual} from 'node:crypto';

export function verifyWebhook(rawBody, headers, apiSecret, now = Math.floor(Date.now() / 1000)) {
  const timestamp = headers['x-timestamp'];
  const signature = headers['x-signature'];
  if (typeof timestamp !== 'string' || !/^[0-9]{10}$/.test(timestamp) ||
      typeof signature !== 'string' || !/^[0-9a-f]{64}$/.test(signature) ||
      Math.abs(now - Number(timestamp)) > 300) return false;
  const expected = createHmac('sha256', apiSecret).update(timestamp + '.').update(rawBody).digest();
  return timingSafeEqual(expected, Buffer.from(signature, 'hex'));
}

// Example with node:http - read the raw bytes first, never re-serialise the JSON.
// import http from 'node:http';
// http.createServer((req, res) => {
//   const chunks = [];
//   req.on('data', (c) => chunks.push(c)).on('end', () => {
//     const raw = Buffer.concat(chunks);
//     const secret = req.headers['x-environment'] === 'live' ? process.env.LIVE_API_SECRET : process.env.SANDBOX_API_SECRET;
//     if (!verifyWebhook(raw, req.headers, secret)) { res.writeHead(401).end(); return; }
//     const event = JSON.parse(raw.toString('utf8'));
//     // Save event.event_id (unique) and the event, then answer quickly.
//     res.writeHead(204).end();
//   });
// }).listen(8080);
<?php
// PHP 8.1+: verify a Payniko notification before parsing it.
function payniko_verify_webhook(string $rawBody, string $timestamp, string $signature, string $apiSecret, ?int $now = null): bool
{
    $now ??= time();
    if (!preg_match('/^[0-9]{10}$/', $timestamp) || !preg_match('/^[0-9a-f]{64}$/', $signature)) return false;
    if (abs($now - (int) $timestamp) > 300) return false;
    $expected = hash_hmac('sha256', $timestamp . '.' . $rawBody, $apiSecret);
    return hash_equals($expected, $signature);
}

$raw = file_get_contents('php://input');            // raw bytes, before json_decode
$environment = $_SERVER['HTTP_X_ENVIRONMENT'] ?? '';
$secret = $environment === 'live' ? getenv('LIVE_API_SECRET') : getenv('SANDBOX_API_SECRET');
if (!payniko_verify_webhook($raw, $_SERVER['HTTP_X_TIMESTAMP'] ?? '', $_SERVER['HTTP_X_SIGNATURE'] ?? '', (string) $secret)) {
    http_response_code(401);
    exit;
}
$event = json_decode($raw, true, 32, JSON_THROW_ON_ERROR);
// Save $event['event_id'] (unique) and the event, then answer quickly.
http_response_code(204);
# Python 3.9+: verify a Payniko notification before parsing it.
import hashlib, hmac, re, time


def verify_webhook(raw_body: bytes, headers, api_secret: str, now=None) -> bool:
    timestamp = headers.get("X-Timestamp", "")
    signature = headers.get("X-Signature", "")
    if not re.fullmatch(r"[0-9]{10}", timestamp) or not re.fullmatch(r"[0-9a-f]{64}", signature):
        return False
    if abs(int(now or time.time()) - int(timestamp)) > 300:
        return False
    expected = hmac.new(api_secret.encode(), timestamp.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature)

# Flask example:
# @app.post("/webhooks/payments")
# def payniko_webhook():
#     raw = request.get_data()                    # raw bytes, before JSON parsing
#     env = request.headers.get("X-Environment")
#     secret = os.environ["LIVE_API_SECRET" if env == "live" else "SANDBOX_API_SECRET"]
#     if not verify_webhook(raw, request.headers, secret):
#         return "", 401
#     event = json.loads(raw)
#     # Save event["event_id"] (unique) and the event, then answer quickly.
#     return "", 204

Επεξεργασία κάθε συμβάντος μία φορά

  1. Αποθηκεύστε το event_id σε πίνακα με μοναδικό ευρετήριο. Αν υπάρχει ήδη, απαντήστε με 2xx και διακόψτε την επεξεργασία.

  2. Απαντήστε με 2xx μέσα σε λίγα δευτερόλεπτα και εκτελέστε τις χρονοβόρες εργασίες (μηνύματα ηλεκτρονικού ταχυδρομείου, ενημερώσεις αποθέματος) στη συνέχεια, μέσω ουράς.

  3. Τα συμβάντα μπορεί να φτάσουν με καθυστέρηση, περισσότερες από μία φορές ή εκτός σειράς. Μια πληρωμή που ολοκληρώνεται λόγω λήξης χρονικού ορίου μπορεί να αναφερθεί ένα λεπτό ή και αργότερα. Βασίζετε τις αποφάσεις σας στην κατάσταση που περιέχει το data ή ανακτήστε ξανά το αντικείμενο με GET.

  4. Μην εκτελείτε ποτέ μια παραγγελία μόνο επειδή το πρόγραμμα περιήγησης επέστρεψε στο success_url.

Επαναλήψεις αποστολής

  • Κάθε απάντηση 2xx εντός περίπου 10 δευτερολέπτων θεωρείται επιτυχής αποστολή. Οι ανακατευθύνσεις δεν ακολουθούνται και θεωρούνται αποτυχίες.
  • Οι αποτυχημένες αποστολές επαναλαμβάνονται μετά από περίπου 1 λεπτό, 5 λεπτά, 15 λεπτά, 1 ώρα και 6 ώρες. Το σώμα και το X-Event-Id παραμένουν αμετάβλητα· τα X-Timestamp και X-Signature δημιουργούνται εκ νέου σε κάθε προσπάθεια.
  • Μετά την τελευταία προσπάθεια, η αποστολή επισημαίνεται ως failed. Ορισμένα σφάλματα (ιδιωτική IP ως προορισμός, κλειδί υπογραφής που λείπει) δεν επαναλαμβάνονται και η αποστολή επισημαίνεται αμέσως ως failed. Οι αποτυχημένες αποστολές μπορούν να σταλούν ξανά μέσω του API αποστολών.
  • Το webhook_url σας πρέπει να επιλύεται σε δημόσια διεύθυνση IP σε κάθε προσπάθεια.

Αποστολές webhook

Κάθε ειδοποίηση webhook που αποστέλλεται καταχωρίζεται ως αποστολή. Χρησιμοποιήστε αυτό το API για να εντοπίσετε τις αποτυχημένες αποστολές και να τις στείλετε ξανά.

GET/api/webhooks/deliveries

Λίστα και ανάκτηση αποστολών

./payniko.sh GET '/api/webhooks/deliveries?status=failed&page=1&per_page=20'
import {api} from './payniko.mjs';

const deliveries = await api('GET', '/api/webhooks/deliveries?status=failed&page=1&per_page=20');
console.log(deliveries);
<?php
require __DIR__ . '/payniko.php';

$deliveries = payniko_api('GET', '/api/webhooks/deliveries?status=failed&page=1&per_page=20');
print_r($deliveries);
from payniko import api

deliveries = api("GET", "/api/webhooks/deliveries?status=failed&page=1&per_page=20")
print(deliveries)

Φιλτράρετε με βάση το status (pending, delivered, failed) και χρησιμοποιήστε σελιδοποίηση με τα page και per_page. Η λίστα περιέχει τις αποστολές του περιβάλλοντος του κλειδιού σας. Το GET /api/webhooks/deliveries/{id} επιστρέφει επιπλέον τα url και payload.

ΠεδίοΤύποςΠεριγραφή
idstring · uuidΑναγνωριστικό της αποστολής· ταυτίζεται με το event_id της ειδοποίησης webhook.
eventstringΤο όνομα του συμβάντος, για παράδειγμα payment.succeeded.
statusstringΗ κατάσταση της αποστολής.
pendingdeliveredfailed
payment_idstring · LUVEI-ABCD-123456 | nullΗ σχετική πληρωμή, εφόσον υπάρχει.
subject_typestring | nullrefund, dispute ή withdrawal· null για συμβάντα πληρωμών.
subject_idstring | nullΑναγνωριστικό της επιστροφής χρημάτων, της αμφισβήτησης ή της ανάληψης.
attempt_countintegerΑριθμός προσπαθειών που έχουν πραγματοποιηθεί έως τώρα.
last_status_codeinteger | nullΟ κωδικός κατάστασης HTTP που επέστρεψε το endpoint σας στην τελευταία προσπάθεια.
last_error_codestring | nullΗ αιτία αποτυχίας της τελευταίας προσπάθειας· δείτε τον πίνακα παρακάτω.
last_error_messagestring | nullΠεριγραφή του last_error_code σε αναγνώσιμη μορφή.
last_errorstring | nullΣφάλμα σε ελεύθερο κείμενο, ενδεχομένως στα τουρκικά. Χρησιμοποιήστε αντί αυτού το last_error_code.
next_attempt_atstring · date-time | nullΧρόνος της επόμενης αυτόματης προσπάθειας.
delivered_atstring · date-time | nullΧρόνος κατά τον οποίο το endpoint σας αποδέχθηκε την αποστολή.
created_atstring · date-timeΧρόνος δημιουργίας της ειδοποίησης webhook.
urlstringΜόνο στην απάντηση λεπτομερειών: το URL προορισμού.
payloadobject | nullΜόνο στην απάντηση λεπτομερειών: το αποθηκευμένο σώμα της ειδοποίησης webhook, επανακωδικοποιημένο (οι αναφορές πληρωμών εμφανίζονται ως payment_id). Δεν αντιστοιχεί στα υπογεγραμμένα byte· μην επαληθεύετε υπογραφές με βάση αυτό.

Απάντηση · Παράδειγμα

{
  "id": "0a6f3c9e-2b7d-4e15-9c84-6d1f2a3b5e70",
  "event": "payment.succeeded",
  "status": "failed",
  "payment_id": "LUVEI-PAYN-123456",
  "subject_type": null,
  "subject_id": null,
  "attempt_count": 6,
  "last_status_code": 500,
  "last_error": "Bildirim adresi başarısız yanıt verdi.",
  "last_error_code": "webhook_response_unsuccessful",
  "last_error_message": "Webhook delivery failed.",
  "next_attempt_at": null,
  "delivered_at": null,
  "created_at": "2026-11-01T12:02:00.000Z"
}
ΚωδικόςΣημασία
webhook_response_unsuccessfulΤο endpoint σας απάντησε με κωδικό κατάστασης εκτός 2xx (δείτε το last_status_code) ή με ανακατεύθυνση.
webhook_unreachableΔεν ήταν δυνατή η πρόσβαση στο endpoint σας (DNS, σύνδεση ή λήξη χρονικού ορίου).
invalid_webhook_targetΤο αποθηκευμένο URL δεν είναι έγκυρο URL webhook.
webhook_target_private_networkΤο URL επιλύθηκε σε ιδιωτική ή τοπική διεύθυνση IP.
webhook_environment_invalidΔεν ήταν δυνατό να προσδιοριστεί το περιβάλλον της ειδοποίησης webhook.
webhook_signing_unavailableΔεν ήταν δυνατή η υπογραφή της ειδοποίησης webhook.
webhook_egress_proxy_unreachableΠροσωρινό πρόβλημα αποστολής από την πλευρά του Payniko.
webhook_delivery_failedΟποιοδήποτε άλλο σφάλμα αποστολής.
POST/api/webhooks/deliveries/{id}/retryΑπαιτείται Idempotency-Key

Επανάληψη αποτυχημένης αποστολής

KEY="$(openssl rand -hex 16)"  # store with the order before sending; reuse it after network errors
./payniko.sh POST '/api/webhooks/deliveries/0a6f3c9e-2b7d-4e15-9c84-6d1f2a3b5e70/retry' '' "$KEY"
import {api, randomUUID} from './payniko.mjs';

const key = randomUUID(); // Store this key with the order before sending; reuse it after network errors.
const delivery = await api('POST', '/api/webhooks/deliveries/0a6f3c9e-2b7d-4e15-9c84-6d1f2a3b5e70/retry', undefined, key);
console.log(delivery);
<?php
require __DIR__ . '/payniko.php';

$key = bin2hex(random_bytes(16)); // store with the order; reuse it after network errors
$delivery = payniko_api('POST', '/api/webhooks/deliveries/0a6f3c9e-2b7d-4e15-9c84-6d1f2a3b5e70/retry', null, $key);
print_r($delivery);
from payniko import api
import uuid

key = str(uuid.uuid4())  # store with the order; reuse it after network errors
delivery = api("POST", "/api/webhooks/deliveries/0a6f3c9e-2b7d-4e15-9c84-6d1f2a3b5e70/retry", None, key)
print(delivery)

Απάντηση · 200

Επιστρέφει την αποστολή με status: pending.

  • Μόνο οι αποστολές σε failed μπορούν να σταλούν ξανά· για οποιαδήποτε άλλη αποστολή επιστρέφεται 409 9000025.
  • Η ίδια ειδοποίηση webhook τίθεται ξανά σε ουρά με το ίδιο id, το ίδιο event_id και το ίδιο σώμα, και ξεκινά νέα σειρά προσπαθειών.
  • Στείλτε κενό σώμα ή {}.

Πιθανά σφάλματα

Sandbox και δοκιμαστικές κάρτες

Δοκιμάστε την ενσωμάτωσή σας με κλειδί sandbox πριν από τη μετάβαση σε παραγωγή. Το sandbox παρέχει το ίδιο API και τις ίδιες ειδοποιήσεις webhook με το περιβάλλον παραγωγής, χωρίς πραγματικά κεφάλαια και χωρίς πραγματική τραπεζική επεξεργασία.

Λειτουργία του sandbox

  • Δεν υπάρχει διακόπτης περιβάλλοντος: τα αιτήματα που υπογράφονται με κλειδί sandbox εκτελούνται στο sandbox και τα αιτήματα που υπογράφονται με κλειδί παραγωγής εκτελούνται στο περιβάλλον παραγωγής.
  • Οι παρακάτω κάρτες λειτουργούν τόσο στη σελίδα πληρωμής όσο και στη δική σας φόρμα πληρωμής· οι πραγματικές κάρτες απορρίπτονται. Η δική σας φόρμα δέχεται επίσης οποιονδήποτε άλλο έγκυρο αριθμό κάρτας με BIN 435509 ή 400000· στη συνέχεια, μια δοκιμαστική σελίδα σάς επιτρέπει να επιλέξετε επιτυχία, αποτυχία ή απουσία απάντησης.
  • Οι οθόνες 3-D Secure είναι δοκιμαστικές και οι αριθμοί κάρτας πρέπει να έχουν έγκυρο ψηφίο ελέγχου.
  • Οι ειδοποιήσεις webhook αποστέλλονται ως πραγματικά αιτήματα HTTPS στο webhook_url σας και υπογράφονται με το μυστικό κλειδί sandbox.

Δοκιμαστικές κάρτες

Οι κάρτες συμπεριφέρονται το ίδιο στο Hosted API και στο Direct API, εκτός αν ο πίνακας αναφέρει κάτι διαφορετικό. Χρησιμοποιήστε οποιαδήποτε μελλοντική ημερομηνία λήξης και οποιοδήποτε τριψήφιο CVV.

Αριθμός κάρταςΑποτέλεσμα
4000000000000010Εγκρίνεται χωρίς 3-D Secure. Στο Direct API, η απάντηση δημιουργίας επιστρέφει ήδη succeeded.
4000000000000002 · 4355090000044242Δοκιμαστικό βήμα 3-D Secure και στη συνέχεια succeeded.
4000000000000051 · 4355090000040000Απορρίπτεται πριν από το 3-D Secure (do_not_honor). Hosted API: η πληρωμή μπορεί να παραμείνει σε requires_action για περίπου 5 λεπτά και να στείλει payment.updated πριν καταλήξει σε failed. Direct API: η απάντηση δημιουργίας επιστρέφει ήδη failed.
4000000000000093 · 4355090000005151Δοκιμαστικό βήμα 3-D Secure και στη συνέχεια failed με insufficient_funds.
4000000000000044Χωρίς απάντηση από την τράπεζα και χωρίς 3-D Secure· η πληρωμή παραμένει σε processing.
4355090000089999Hosted API: processing χωρίς βήμα 3-D Secure. Direct API: δοκιμαστικό βήμα 3-D Secure και στη συνέχεια processing.
  • Με οποιαδήποτε δοκιμαστική κάρτα, ένα ποσό που λήγει σε .77 (για παράδειγμα 100.77) διατηρεί την πληρωμή σε processing (χειροκίνητος έλεγχος).
  • Μια δοκιμαστική πληρωμή που δεν λαμβάνει ποτέ απάντηση κλείνει τελικά ως failed με service_unavailable.
  • Μια πληρωμή με δοκιμαστική κάρτα που απορρίπτεται (για παράδειγμα 4000000000000051) μπορεί να παραμείνει σε requires_action για κάποιο διάστημα πριν περιέλθει σε τελική κατάσταση. Βασίζετε τις αποφάσεις σας στην ειδοποίηση webhook ή σε ένα GET, όχι σε μια ενδιάμεση απόρριψη.

Δοκιμαστικά BIN

Στο sandbox, ο έλεγχος BIN επιστρέφει τα ακόλουθα αποτελέσματα:

BINΑποτέλεσμα
435509 · 400000200 με payment_mode: direct.
540061200 με payment_mode: hosted· ο πληρωτής συνεχίζει στη σελίδα πληρωμής του sandbox.
979210409 με κωδικό 0055230.
Οποιαδήποτε άλλα έξι ψηφία409 με κωδικό 0055233.

Επιστροφές χρημάτων και αναλήψεις

Στο sandbox, οι επιστροφές χρημάτων και οι αναλήψεις ολοκληρώνονται αμέσως έναντι ενός δοκιμαστικού υπολοίπου. Επιλέξτε το αποτέλεσμα με την κεφαλίδα X-Sandbox-Scenario, η οποία δεν αποτελεί μέρος της υπογραφής. Χρησιμοποιήστε νέο Idempotency-Key για κάθε σενάριο: η κεφαλίδα αποτελεί μέρος του αποτυπώματος idempotency και το ίδιο κλειδί με διαφορετικό σενάριο απορρίπτεται με 409 9000010. Άγνωστη τιμή, ή η κεφαλίδα με κλειδί live, επιστρέφει 422 1000001· το τελικό σημείο αποδεικτικών στοιχείων δεν δέχεται την κεφαλίδα. Το σενάριο που εφαρμόστηκε επιστρέφεται στην κεφαλίδα απάντησης X-Sandbox-Scenario.

Πριν εφαρμοστεί το σενάριο, το sandbox επιβάλλει τους ίδιους κανόνες με το live: ελάχιστα και ημερήσια όρια ανάληψης, συναλλαγματικές ισοτιμίες (503 3000001 όταν δεν είναι διαθέσιμες) και επαλήθευση διεύθυνσης. Κάθε διεύθυνση TRC20 με έγκυρο άθροισμα ελέγχου γίνεται δεκτή στο sandbox, συμπεριλαμβανομένης της μηδενικής διεύθυνσης TRON, και δεν πραγματοποιείται μεταφορά· το 1000030 (διεύθυνση που δεν μπορεί να λάβει πληρωμές) δεν αναπαράγεται στο sandbox.

EndpointΤιμή
POST /api/account/refundscompleted (προεπιλογή) ή rejected
POST /api/account/withdrawalssent (προεπιλογή) ή rejected
KEY="$(openssl rand -hex 16)"  # store with the order before sending; reuse it after network errors
SANDBOX_SCENARIO=rejected ./payniko.sh POST '/api/account/refunds' '{
  "payment_id": "LUVEI-PAYN-123456",
  "operation_type": "refund",
  "reason": "Test refund"
}' "$KEY"
import {api, randomUUID} from './payniko.mjs';

const key = randomUUID(); // Store this key with the order before sending; reuse it after network errors.
const refund = await api('POST', '/api/account/refunds', {
  "payment_id": "LUVEI-PAYN-123456",
  "operation_type": "refund",
  "reason": "Test refund"
}, key, {'X-Sandbox-Scenario': 'rejected'});
console.log(refund);
<?php
require __DIR__ . '/payniko.php';

$key = bin2hex(random_bytes(16)); // store with the order; reuse it after network errors
$refund = payniko_api('POST', '/api/account/refunds', [
    "payment_id" => 'LUVEI-PAYN-123456',
    "operation_type" => 'refund',
    "reason" => 'Test refund',
], $key, ['X-Sandbox-Scenario: rejected']);
print_r($refund);
from payniko import api
import uuid

key = str(uuid.uuid4())  # store with the order; reuse it after network errors
refund = api("POST", "/api/account/refunds", {
    "payment_id": "LUVEI-PAYN-123456",
    "operation_type": "refund",
    "reason": "Test refund"
}, key, {"X-Sandbox-Scenario": "rejected"})
print(refund)

Το tx_hash στο sandbox είναι προσομοιωμένο και δεν αντιστοιχεί σε πραγματική συναλλαγή blockchain.

POST/api/sandbox/disputesΑπαιτείται Idempotency-KeyΜόνο με κλειδί sandbox

Δημιουργία δοκιμαστικής αμφισβήτησης

Ανοίγει αμφισβήτηση σε επιτυχή πληρωμή sandbox, ώστε να δοκιμάσετε τον χειρισμό αμφισβητήσεων και τις ειδοποιήσεις webhook. Διατίθεται μόνο με κλειδί sandbox· με κλειδί παραγωγής επιστρέφεται 409 9000013.

ΠεδίοΤύποςΥποχρεωτικόΠεριγραφή
payment_idstring · LUVEI-ABCD-123456ΥποχρεωτικόΜία από τις πληρωμές sandbox σας με κατάσταση succeeded.
reasonstringΥποχρεωτικόΗ αιτία της αμφισβήτησης.
fraudunauthorizedproduct_not_deliveredduplicateamount_mismatchother
scenariostringΠροαιρετικόΑρχική κατάσταση. Προεπιλογή: open.
openwonlost
KEY="$(openssl rand -hex 16)"  # store with the order before sending; reuse it after network errors
./payniko.sh POST '/api/sandbox/disputes' '{
  "payment_id": "LUVEI-PAYN-123456",
  "reason": "product_not_delivered",
  "scenario": "open"
}' "$KEY"
import {api, randomUUID} from './payniko.mjs';

const key = randomUUID(); // Store this key with the order before sending; reuse it after network errors.
const dispute = await api('POST', '/api/sandbox/disputes', {
  "payment_id": "LUVEI-PAYN-123456",
  "reason": "product_not_delivered",
  "scenario": "open"
}, key);
console.log(dispute);
<?php
require __DIR__ . '/payniko.php';

$key = bin2hex(random_bytes(16)); // store with the order; reuse it after network errors
$dispute = payniko_api('POST', '/api/sandbox/disputes', [
    "payment_id" => 'LUVEI-PAYN-123456',
    "reason" => 'product_not_delivered',
    "scenario" => 'open',
], $key);
print_r($dispute);
from payniko import api
import uuid

key = str(uuid.uuid4())  # store with the order; reuse it after network errors
dispute = api("POST", "/api/sandbox/disputes", {
    "payment_id": "LUVEI-PAYN-123456",
    "reason": "product_not_delivered",
    "scenario": "open"
}, key)
print(dispute)

Απάντηση · 201

Επιστρέφει το αντικείμενο αμφισβήτησης. Μια δοκιμαστική αμφισβήτηση σε open δέχεται απαντήσεις για 7 ημέρες· οι καταστάσεις won και lost είναι αμέσως τελικές. Η κατάσταση under_review δεν εμφανίζεται στο sandbox και οι δοκιμαστικές αμφισβητήσεις δεν επηρεάζουν το υπόλοιπό σας.

Αρχείο αιτημάτων

Η σελίδα Αρχείο αιτημάτων παραθέτει τα πρόσφατα αιτήματα API σας με την κατάσταση, τον κωδικό σφάλματος και το correlation_id. Τα αιτήματα που απορρίπτονται πριν από την αναγνώριση του κλειδιού (μη έγκυρο κλειδί, υπογραφή ή κωδικός εμπόρου) δεν εμφανίζονται. Οι πληρωμές sandbox εμφανίζονται στην ενότητα Παραγγελίες API Sandbox.

Κωδικοί σφαλμάτων

Τα σφάλματα επιστρέφονται ως application/problem+json. Βασίζετε τη λογική σας στον κωδικό κατάστασης HTTP και στον επταψήφιο code· τα title και detail προορίζονται για ανθρώπινους αναγνώστες.

Μορφή απάντησης σφάλματος

ΠεδίοΠεριγραφή
typeURL του τύπου σφάλματος: https://payniko.com/problems/{code}.
titleΣύντομη περίληψη του σφάλματος.
statusΚωδικός κατάστασης HTTP.
detailΕπεξήγηση του σφάλματος στο συγκεκριμένο αίτημα.
codeΣταθερός επταψήφιος κωδικός· χρησιμοποιήστε τον στη λογική σας.
correlation_idΤαυτίζεται με την κεφαλίδα X-Correlation-Id. Συμπεριλάβετέ το στα αιτήματα υποστήριξης.
errorsΜηνύματα ανά πεδίο για σφάλματα επικύρωσης: { "field": ["message"] }.
integrationΥποδείξεις ενσωμάτωσης: stage, reason, violations (location, path, expected, hint), docs_url, retryable και, σε ορισμένες περιπτώσεις, retry_after.
metadataΜπορεί να υπάρχει σε σφάλματα του Direct API, καθώς και σε σφάλματα ελέγχου ταυτότητας και ορίου ρυθμού αιτημάτων, με retry_class και ένα ενημερωτικό developer_detail. Αντιμετωπίστε το ως προαιρετικό.
limitsΕπιτρεπόμενο εύρος ποσού στο 9000005. Στο 0055235, το αντίστοιχο πεδίο είναι το amount_limit.
retry_afterΑριθμός δευτερολέπτων αναμονής· υπάρχει σε ορισμένα σφάλματα 503.

HTTP 422 · 1000001

{
  "type": "https://payniko.com/problems/1000001",
  "title": "Validation failed",
  "status": 422,
  "detail": "The submitted fields are not valid.",
  "code": "1000001",
  "correlation_id": "9b8c7d6e-5f40-4a3b-8c2d-1e0f9a8b7c6d",
  "errors": {
    "merchant_order_id": [
      "This field is required."
    ]
  },
  "integration": {
    "stage": "validation",
    "reason": "validation_failed",
    "violations": [
      {
        "location": "body",
        "path": "merchant_order_id",
        "expected": {
          "type": "string",
          "min_length": 1,
          "max_length": 64,
          "unique_within": "account_and_environment"
        },
        "hint": "Check this field against the documented format."
      }
    ],
    "docs_url": "/en/developers#errors",
    "retryable": false
  }
}

Οδηγίες επανάληψης

retry_classΣημασία
no_retryΔιορθώστε το αίτημα πριν το στείλετε ξανά.
retry_same_requestΣτείλτε ξανά το πανομοιότυπο αίτημα μετά από σύντομη αναμονή (ίδιο κλειδί, ίδιο σώμα).
retry_afterΠεριμένετε το διάστημα που ορίζει το Retry-After και, στη συνέχεια, στείλτε ξανά το αίτημα.
read_orderΑνακτήστε την πληρωμή με GET πριν αποφασίσετε πώς θα προχωρήσετε.
  • Το retry_class υπάρχει μόνο σε απαντήσεις που περιέχουν metadata (Direct API, σφάλματα ταυτοποίησης και ορίου ρυθμού). Τα υπόλοιπα σφάλματα περιέχουν αντ' αυτού integration.retryable.
  • Μετά από λήξη χρονικού ορίου ή 5xx, μη δημιουργείτε ποτέ νέα πληρωμή· ανακτήστε την πληρωμή ή στείλτε ξανά το αίτημα με το ίδιο κλειδί.
  • Η απόρριψη κάρτας δεν αποτελεί σφάλμα HTTP: η πληρωμή επιστρέφεται με 200, status: failed και failure.
  • Το developer_detail ενδέχεται να αλλάξει· μη βασίζετε τη λογική της ενσωμάτωσης σε αυτό.

Όλοι οι κωδικοί σφαλμάτων

Έλεγχος ταυτότητας και πρόσβαση

ΚωδικόςHTTPΣημασίαΣυνιστώμενη ενέργεια
0055502401 / 403Η πρόσβαση απορρίφθηκε. 401: λείπει η κεφαλίδα X-Api-Key ή X-Merchant-Code, μια κεφαλίδα έχει σταλεί δύο φορές ή έχει μη έγκυρη μορφή, το κλειδί είναι άγνωστο ή έχει ανακληθεί, ο κωδικός εμπόρου δεν αντιστοιχεί ή στάλθηκε X-Environment που δεν αντιστοιχεί στο κλειδί. 403: ο λογαριασμός ή η πρόσβαση στο Direct API δεν είναι ενεργή.Επαληθεύστε τις κεφαλίδες και το κλειδί API. Μην επαναλαμβάνετε το αίτημα αυτόματα.
0055504401Η κεφαλίδα X-Timestamp ή X-Signature λείπει ή έχει μη έγκυρη μορφή, ή το ρολόι του διακομιστή σας αποκλίνει από την ώρα διακομιστή του Payniko περισσότερο από 300 δευτερόλεπτα.Διορθώστε το ρολόι του διακομιστή σας (NTP) και υπογράψτε ξανά το αίτημα με τρέχουσα χρονική σήμανση.
0055505401Η υπογραφή δεν αντιστοιχεί στο αίτημα.Υπολογίστε την τιμή κατακερματισμού ακριβώς των byte του σώματος που αποστέλλετε, υπογράψτε τη διαδρομή χωρίς τη συμβολοσειρά ερωτήματος και χρησιμοποιήστε το σωστό μυστικό κλειδί API.
9000002401Το αίτημα είχε ήδη λήξει τη στιγμή της επεξεργασίας του.Υπογράψτε ξανά το αίτημα με τρέχουσα χρονική σήμανση και στείλτε το εκ νέου.
9000040403Το Hosted API δεν είναι ενεργοποιημένο για τον λογαριασμό σας.Επικοινωνήστε με την υποστήριξη.
1000004403Η δημιουργία νέων πληρωμών έχει ανασταλεί προσωρινά.Επαναλάβετε το αίτημα αργότερα.
9000004403Ο λογαριασμός σας έχει περιορισμό για αυτή τη λειτουργία.Επικοινωνήστε με την υποστήριξη.

Idempotency-Key και συγκρούσεις

ΚωδικόςHTTPΣημασίαΣυνιστώμενη ενέργεια
1000009422Η κεφαλίδα Idempotency-Key λείπει ή δεν είναι έγκυρη.Σε κάθε POST στείλτε κλειδί 8–128 χαρακτήρων από το σύνολο A-Z a-z 0-9 _ -.
9000010409Αυτό το Idempotency-Key έχει ήδη χρησιμοποιηθεί για διαφορετικό αίτημα ή το ίδιο υπογεγραμμένο αίτημα στάλθηκε ξανά με διαφορετικό κλειδί.Επαναλάβετε το αίτημα με το ίδιο κλειδί και το ίδιο σώμα· χρησιμοποιήστε νέο κλειδί μόνο για νέα λειτουργία.
9000039409Το πρώτο αίτημα με αυτό το Idempotency-Key βρίσκεται ακόμη υπό επεξεργασία (Hosted API).Στείλτε ξανά το ίδιο αίτημα μετά από σύντομη αναμονή.
9000007409Υπάρχει ήδη πληρωμή για αυτό το merchant_order_id (Hosted API).Ανακτήστε την υπάρχουσα πληρωμή ή χρησιμοποιήστε νέο κωδικό παραγγελίας.
0055503409Σύγκρουση στο Direct API: υπάρχει ήδη πληρωμή για αυτό το merchant_order_id, το Idempotency-Key χρησιμοποιήθηκε με διαφορετικό σώμα ή για πληρωμή Hosted API, ή το αρχικό αίτημα βρίσκεται ακόμη σε εξέλιξη.Ανακτήστε την πληρωμή με GET. Στείλτε ξανά μόνο πανομοιότυπο αίτημα.

Επικύρωση δεδομένων

ΚωδικόςHTTPΣημασίαΣυνιστώμενη ενέργεια
1000001422Το αίτημα δεν είναι έγκυρο: άγνωστο πεδίο, μη έγκυρη μορφή, συμβολοσειρά ερωτήματος σε POST, παράμετρος ερωτήματος που δεν υποστηρίζεται ή σώμα που δεν είναι αντικείμενο JSON. Το errors παραθέτει τα πεδία που αφορά το σφάλμα.Διορθώστε τα πεδία που αναφέρονται.
1000020422Η μορφή του ποσού δεν είναι έγκυρη.Στείλτε θετικό ποσό ως συμβολοσειρά, με δύο δεκαδικά ψηφία και χωρίς αρχικά μηδενικά, για παράδειγμα "100.00".
1000021422Το νόμισμα δεν υποστηρίζεται.Χρησιμοποιήστε USD, EUR, AED ή TRY ή παραλείψτε το currency.
1000023422Το ποσό είναι υπερβολικά μεγάλο.Στείλτε μικρότερο ποσό.
9000008422Το webhook_url δεν είναι έγκυρο.Χρησιμοποιήστε δημόσιο URL HTTPS στη θύρα 443 με μέγιστο μήκος 500 χαρακτήρων.
9000038422Το success_url ή το fail_url δεν είναι έγκυρο.Χρησιμοποιήστε URL HTTPS με μέγιστο μήκος 2048 χαρακτήρων. Το sandbox δέχεται επίσης http://localhost.

Ποσό, κάρτα και BIN

ΚωδικόςHTTPΣημασίαΣυνιστώμενη ενέργεια
9000005422Το ποσό βρίσκεται εκτός των ορίων του λογαριασμού σας (Hosted API). Το limits περιέχει το επιτρεπόμενο εύρος.Προσαρμόστε το ποσό.
0055235422Το ποσό δεν γίνεται δεκτό (Direct API και έλεγχος BIN). Το amount_limit περιέχει τα min και max, όταν είναι διαθέσιμα.Προσαρμόστε το ποσό.
0055210422Το BIN δεν αποτελείται από έξι ψηφία.Στείλτε τα πρώτα έξι ψηφία του αριθμού κάρτας.
0055233409Το BIN δεν είναι γνωστό.Ζητήστε από τον πληρωτή άλλη κάρτα. Δεν πρόκειται για απόρριψη από την τράπεζα.
0055230409Αυτή η κάρτα δεν μπορεί να χρησιμοποιηθεί για αυτή την πληρωμή.Ζητήστε από τον πληρωτή άλλη κάρτα. Δεν πρόκειται για απόρριψη από την τράπεζα.
0055231409Αυτή η κάρτα δεν γίνεται δεκτή για αυτή την πληρωμή.Ζητήστε από τον πληρωτή άλλη κάρτα.
0055221409Έχει συμπληρωθεί ένα όριο αυτής της κάρτας.Χρησιμοποιήστε άλλη κάρτα ή επαναλάβετε το αίτημα αργότερα.
0055234409Τα στοιχεία της κάρτας δεν είναι έγκυρα: αριθμός κάρτας, ημερομηνία λήξης ή CVV. Το metadata.developer_detail προσδιορίζει το πεδίο: card_number_invalid, card_expiry_invalid ή card_cvv_invalid.Ζητήστε από τον πληρωτή να εισαγάγει ξανά τα στοιχεία της κάρτας.
0055001409Ο λογαριασμός σας δεν είναι έτοιμος για αυτή την πληρωμή. Με κλειδί sandbox, αυτό σημαίνει επίσης ότι η προσομοίωση sandbox δεν είναι προσωρινά διαθέσιμη (developer_detail: sandbox_unavailable), ισοδύναμο με 9000013.Επικοινωνήστε με την υποστήριξη.
0055002403Οι πληρωμές με κάρτα δεν είναι ενεργοποιημένες για τον λογαριασμό σας.Επικοινωνήστε με την υποστήριξη.
9000013409Η λειτουργία sandbox δεν είναι διαθέσιμη, για παράδειγμα όταν ένα endpoint αποκλειστικά για sandbox καλείται με κλειδί παραγωγής.Χρησιμοποιήστε κλειδί sandbox ή επαναλάβετε το αίτημα αργότερα.

Δεν βρέθηκε

ΚωδικόςHTTPΣημασίαΣυνιστώμενη ενέργεια
9000009404Η πληρωμή δεν υπάρχει σε αυτόν τον λογαριασμό και το περιβάλλον ή δεν μπορεί να χρησιμοποιηθεί για αυτή τη λειτουργία.Επαληθεύστε το payment_id και χρησιμοποιήστε κλειδί του ίδιου περιβάλλοντος.
1000002404Δεν βρέθηκε: η διαδρομή είναι άγνωστη ή αυτό το προϊόν API δεν είναι ενεργοποιημένο στην πλατφόρμα. Ένα API απενεργοποιημένο για τον λογαριασμό σας επιστρέφει αντ' αυτού 403 (9000040 στο Hosted API, 0055502 στο Direct API).Επαληθεύστε τη διαδρομή· επικοινωνήστε με την υποστήριξη αν το API θα έπρεπε να είναι διαθέσιμο.
1000007405Η μέθοδος HTTP δεν επιτρέπεται. Η κεφαλίδα Allow παραθέτει τις έγκυρες μεθόδους.Χρησιμοποιήστε τη μέθοδο που ορίζεται στην τεκμηρίωση.

Όρια και διαθεσιμότητα

ΚωδικόςHTTPΣημασίαΣυνιστώμενη ενέργεια
1000005429Υπερβολικά πολλά αιτήματα ή προσωρινό κλείδωμα μετά από επανειλημμένες αποτυχίες υπογραφής.Περιμένετε το διάστημα που ορίζει το Retry-After και, στη συνέχεια, επαναλάβετε το αίτημα με προοδευτικά αυξανόμενη αναμονή.
1000011503Η υπηρεσία δεν είναι προσωρινά διαθέσιμη.Επαναλάβετε το ίδιο αίτημα. Ανακτήστε τους υπάρχοντες πόρους πριν δημιουργήσετε νέους.
1000013503Αυτή η λειτουργία δεν είναι ακόμη διαθέσιμη.Επαναλάβετε το αίτημα μετά από retry_after δευτερόλεπτα.
1000014503Βρίσκεται σε εξέλιξη προγραμματισμένη συντήρηση. Λεπτομέρειες παρέχουν τα until και note.Επαναλάβετε το αίτημα μετά το διάστημα που ορίζει το Retry-After.
3000001503Η συναλλαγματική ισοτιμία δεν είναι προσωρινά διαθέσιμη.Επαναλάβετε το αίτημα αργότερα.
1000006500Παρουσιάστηκε μη αναμενόμενο σφάλμα διακομιστή.Επαναλάβετε το ίδιο αίτημα ή ανακτήστε τον πόρο.

Λειτουργίες λογαριασμού

ΚωδικόςHTTPΣημασίαΣυνιστώμενη ενέργεια
9000014409Για αυτή την πληρωμή υπάρχει ανοιχτή ή ολοκληρωμένη επιστροφή χρημάτων ή ακύρωση, ή αμφισβήτηση που δεν έχει κριθεί υπέρ σας.Ανακτήστε την επιστροφή χρημάτων ή την αμφισβήτηση.
9000015404Η αμφισβήτηση δεν υπάρχει.Επαληθεύστε το αναγνωριστικό της αμφισβήτησης.
9000016409Η αμφισβήτηση δεν δέχεται πλέον απάντηση (έχει κλείσει ή έχει παρέλθει το evidence_due_at).Ανακτήστε την αμφισβήτηση.
9000017422Η διεύθυνση TRC20 δεν είναι έγκυρη.Επαληθεύστε τη διεύθυνση με τον κάτοχό της.
1000030422Αυτή η διεύθυνση δεν μπορεί να λάβει αναλήψεις.Χρησιμοποιήστε άλλη διεύθυνση.
9000018422Το address_acknowledged δεν είναι true.Επιβεβαιώστε τη διεύθυνση και, στη συνέχεια, στείλτε true.
9000019422Το ποσό ανάληψης δεν είναι έγκυρο ή είναι κατώτερο του ελάχιστου.Προσαρμόστε το ποσό.
9000020409Το διαθέσιμο υπόλοιπο δεν επαρκεί.Ελέγξτε το διαθέσιμο υπόλοιπο.
9000021409Έχει συμπληρωθεί το ημερήσιο όριο αναλήψεων.Επαναλάβετε το αίτημα την επόμενη ημέρα ή ζητήστε μικρότερο ποσό.
9000022409Βρίσκεται ήδη σε επεξεργασία άλλη ανάληψη.Περιμένετε και, στη συνέχεια, στείλτε ξανά το ίδιο αίτημα.
9000023404Η ανάληψη δεν υπάρχει.Επαληθεύστε το αναγνωριστικό της ανάληψης.
9000024404Η αποστολή webhook δεν υπάρχει.Επαληθεύστε το αναγνωριστικό της αποστολής.
9000025409Μόνο οι αποτυχημένες αποστολές μπορούν να σταλούν ξανά.Ελέγξτε την κατάσταση της αποστολής.

Αποκλειστικά στο sandbox

ΚωδικόςHTTPΣημασίαΣυνιστώμενη ενέργεια
9000026503Το sandbox δεν είναι ακόμη διαθέσιμο για αυτή τη λειτουργία.Επαναλάβετε το αίτημα μετά το διάστημα που ορίζει το Retry-After.
9000027409Το δοκιμαστικό υπόλοιπο χρησιμοποιείται από άλλη λειτουργία.Στείλτε ξανά το ίδιο αίτημα μετά από σύντομη αναμονή.
9000030409Δεν ήταν δυνατή η πίστωση του δοκιμαστικού υπολοίπου.Επαναλάβετε το αίτημα αργότερα.
9000033409Το δοκιμαστικό υπόλοιπο δεν επαρκεί για αυτή την επιστροφή χρημάτων. Μια ανάληψη sandbox με ανεπαρκές υπόλοιπο επιστρέφει αντ' αυτού 9000020.Δημιουργήστε πρώτα επιπλέον δοκιμαστικές πληρωμές.

Γλώσσα μηνυμάτων

Τα title και detail ακολουθούν την κεφαλίδα Accept-Language (en, tr, de, ar, es, el· το de-AT αντιμετωπίζεται ως de). Για οποιαδήποτε άλλη τιμή επιστρέφονται αγγλικά. Η κεφαλίδα Content-Language δηλώνει τη γλώσσα που χρησιμοποιήθηκε.

Λίστα ελέγχου για μετάβαση σε παραγωγή

Ολοκληρώστε αυτή τη λίστα ελέγχου πριν δεχτείτε πραγματικές πληρωμές.

  • Στο sandbox, μία πληρωμή έχει καταλήξει σε succeeded και μία άλλη σε failed, και οι καταστάσεις των παραγγελιών σας ενημερώθηκαν αναλόγως.

  • Το endpoint webhook σας επαληθεύει την υπογραφή με βάση το ακατέργαστο σώμα, απορρίπτει παρωχημένες χρονικές σημάνσεις, αποθηκεύει κάθε event_id μία φορά και απαντά άμεσα με 2xx.

  • Εκτελείτε τις παραγγελίες μόνο αφού μια επαληθευμένη ειδοποίηση webhook ή ένα GET δείξει succeeded, ποτέ με βάση την επιστροφή του προγράμματος περιήγησης.

  • Κάθε POST αποθηκεύει το Idempotency-Key του πριν από την αποστολή και το επαναχρησιμοποιεί μετά από σφάλματα δικτύου.

  • Ο κώδικάς σας χειρίζεται τα requires_action, processing, 429 και 5xx χωρίς να δημιουργεί διπλότυπες πληρωμές.

  • Το κλειδί παραγωγής και το μυστικό του κλειδί φυλάσσονται μόνο στο σύστημα διαχείρισης μυστικών σας. Η αντικατάσταση του κλειδιού sandbox με το κλειδί παραγωγής είναι η μόνη αλλαγή στον κώδικα.

  • Τα webhook_url, success_url και fail_url παραπέμπουν στις διευθύνσεις HTTPS του περιβάλλοντος παραγωγής σας.

  • Καταγράφετε το X-Correlation-Id για κάθε αίτημα.

Ακόμη και με έτοιμα κλειδιά, ελέγξτε την ενσωμάτωση με αυτή τη λίστα πριν από πραγματικές πληρωμές. Η δοκιμή δεν αποτελεί προϋπόθεση δημιουργίας κλειδιών.