Με το Direct API, η φόρμα κάρτας αποτελεί μέρος του ιστότοπού σας και ο διακομιστής σας υποβάλλει τα στοιχεία της κάρτας στο Payniko. Το πρόγραμμα περιήγησης του πληρωτή ανακατευθύνεται στο Payniko μόνο για το 3-D Secure και για BIN τύπου hosted. Χρησιμοποιήστε το Direct API όταν η φόρμα κάρτας πρέπει να βρίσκεται στη δική σας σελίδα.
Βήματα ενσωμάτωσης
Έλεγχος BIN
Μόλις ο πληρωτής εισαγάγει έξι ψηφία, καλέστε το GET /api/payments/bin/{bin6}. Το payment_mode υποδεικνύει αν πρέπει να συλλέξετε τα πλήρη στοιχεία της κάρτας στη φόρμα σας ή να παραπέμψετε τον πληρωτή στη σελίδα πληρωμής.
Δημιουργία της πληρωμής
Καλέστε το POST /api/payments/direct με τα στοιχεία της κάρτας ή, για BIN τύπου hosted, μόνο με bin6.
Ολοκλήρωση του 3-D Secure
Αν η κατάσταση είναι requires_action, ανακατευθύνετε το πρόγραμμα περιήγησης στο next_action.url. Στη συνέχεια, το πρόγραμμα περιήγησης επιστρέφει στο success_url ή στο fail_url σας με υπογεγραμμένες παραμέτρους.
Επιβεβαίωση του αποτελέσματος
Περιμένετε την ειδοποίηση webhook payment.succeeded ή payment.failed ή καλέστε το GET /api/payments/{payment_id}. Εκτελέστε την παραγγελία μόνο σε κατάσταση succeeded.
Ροή πληρωμής
Your server Payniko Payer's browser
1 GET /api/payments/bin/{bin6} ───► payment_mode: direct | hosted
2 POST /api/payments/direct ─────► 200 requires_action + next_action.url
(or processing / succeeded / failed)
3 redirect to next_action.url ──────────────────────────────────► 3-D Secure page
───────────────────────────► success_url / fail_url
4 webhook ◄─────────────────────── payment.succeeded / payment.failed
GET /api/payments/{payment_id} ──► current status
Προαπαιτούμενα
Το Direct API ενεργοποιείται ανά λογαριασμό. Χωρίς αυτή την πρόσβαση, τα αιτήματα απορρίπτονται με 4030055502.
Πριν δημιουργήσετε την πληρωμή, ο έλεγχος BIN υποδεικνύει αν η πληρωμή με την κάρτα μπορεί να γίνει στη φόρμα σας (direct) ή πρέπει να ολοκληρωθεί στη σελίδα πληρωμής του Payniko (hosted). Ο έλεγχος δεν συνεπάγεται καμία χρέωση.
Παράμετροι
Πεδίο
Τύπος
Υποχρεωτικό
Περιγραφή
Παράδειγμα
bin6
string · path
Υποχρεωτικό
Τα πρώτα έξι ψηφία της κάρτας.
540061
amount
string · query
Υποχρεωτικό
Ποσό με δύο δεκαδικά ψηφία, για παράδειγμα 100.00. Ελέγχεται έναντι των ορίων του λογαριασμού σας.
100.00
currency
string · query
Προαιρετικό
USD, EUR, AED ή TRY. Προεπιλογή είναι το κύριο νόμισμα του συστήματος. Χρησιμοποιήστε το ίδιο νόμισμα κατά τη δημιουργία της πληρωμής.
USD
Οι λοιπές παράμετροι ερωτήματος απορρίπτονται. Η συμβολοσειρά ερωτήματος δεν αποτελεί μέρος της υπογραφής.
./payniko.sh GET '/api/payments/bin/540061?amount=100.00¤cy=USD'
Το πρόγραμμα της κάρτας. Προς το παρόν είναι πάντα null.
country
string | null
Η χώρα έκδοσης (ISO 3166-1 alpha-2), αν είναι γνωστή.
domestic
boolean
true για εγχώρια κάρτα· false σε κάθε άλλη περίπτωση ή αν δεν είναι γνωστό.
payment_mode
string | null
direct: συλλέξτε τα πλήρη στοιχεία της κάρτας. hosted: παραπέμψτε τον πληρωτή στη σελίδα πληρωμής. Οποιαδήποτε άλλη τιμή, συμπεριλαμβανομένου του null: μη δημιουργήσετε πληρωμή.
directhosted
Χειρισμός της απάντησης
direct: συλλέξτε τα πλήρη στοιχεία της κάρτας και δημιουργήστε την πληρωμή με card.
hosted: δημιουργήστε την πληρωμή μόνο με bin6. Ο πληρωτής εισάγει τα στοιχεία της κάρτας στη σελίδα πληρωμής του Payniko.
Οποιοδήποτε άλλο payment_mode ή σφάλμα κάρτας 409 (0055230, 0055231, 0055233): μη δημιουργήσετε πληρωμή και ζητήστε από τον πληρωτή άλλη κάρτα με ουδέτερο μήνυμα, καθώς δεν πρόκειται για απόρριψη από την τράπεζα. Το 4220055235 υποδηλώνει πρόβλημα με το ποσό. Σε περίπτωση 429 ή 5xx, επαναλάβετε τον ίδιο έλεγχο αργότερα.
Παράδειγμα
import {api} from './payniko.mjs';
let mode = null;
try {
const lookup = await api('GET', '/api/payments/bin/411111?amount=100.00¤cy=USD');
mode = lookup.data.payment_mode;
} catch (error) {
// No answer, 429 or 5xx: temporary. Try the same lookup again later.
if (error.status === undefined || error.status === 429 || error.status >= 500) throw error;
// Other 4xx: error.problem.code says why, for example 0055233.
}
if (mode !== 'direct' && mode !== 'hosted') {
// Also null: do not create a payment. Ask for another card with a neutral message.
}
Επιστρέφει πάντα 200 με την τρέχουσα πληρωμή, ακόμη και όταν το ίδιο αίτημα αποστέλλεται ξανά. Η απόρριψη κάρτας δεν αποτελεί σφάλμα HTTP· η απάντηση είναι 200 με status: failed.
Σώμα αιτήματος
Πεδίο
Τύπος
Υποχρεωτικό
Περιγραφή
Παράδειγμα
merchant_order_id
string
Υποχρεωτικό
Ο κωδικός της παραγγελίας σας: 1–64 χαρακτήρες από το σύνολο A-Z a-z 0-9 . _ -. Μία πληρωμή ανά κωδικό και περιβάλλον· η μοναδικότητα ελέγχεται χωρίς διάκριση πεζών-κεφαλαίων.
ORDER-2026-002
amount
string
Υποχρεωτικό
Ποσό με δύο δεκαδικά ψηφία, για παράδειγμα "100.00", εντός των ορίων του λογαριασμού σας.
"100.00"
currency
string
Προαιρετικό
Προεπιλογή είναι το κύριο νόμισμα του συστήματος. Χρησιμοποιήστε το ίδιο νόμισμα με τον έλεγχο BIN.
USDEURAEDTRY
USD
webhook_url
string · uri
Υποχρεωτικό
Δημόσιο URL HTTPS στη θύρα 443 που λαμβάνει τα webhooks αυτής της πληρωμής, έως 500 χαρακτήρες. Τοπικές διευθύνσεις δεν γίνονται δεκτές, ούτε στο sandbox· το όνομα κεντρικού υπολογιστή πρέπει να επιλύεται σε δημόσια διεύθυνση IP τη στιγμή του αιτήματος, αλλιώς 4229000008.
https://merchant.example/webhooks/payments
success_url
string · uri
Υποχρεωτικό
Προορισμός ανακατεύθυνσης του προγράμματος περιήγησης όταν η πληρωμή έχει ολοκληρωθεί επιτυχώς. HTTPS, έως 2048 χαρακτήρες, χωρίς στοιχεία χρήστη (userinfo) ή αναγνωριστικό τμήματος (fragment). Το sandbox δέχεται επίσης http://localhost, 127.0.0.1 και [::1].
https://merchant.example/payment/success
fail_url
string · uri
Υποχρεωτικό
Προορισμός ανακατεύθυνσης του προγράμματος περιήγησης σε κάθε άλλη περίπτωση. Ισχύουν οι ίδιοι κανόνες με το success_url.
https://merchant.example/payment/failure
customer
object
Υποχρεωτικό
Τα στοιχεία επικοινωνίας του πληρωτή· χρησιμοποιούνται για το αποτέλεσμα της πληρωμής, την απόδειξη και τα βήματα επαλήθευσης της τράπεζας. Γίνονται δεκτά μόνο τα email και phone· κάθε άλλο πεδίο μέσα στο customer απορρίπτεται με 4221000001.
{ … }
customer.email
string
Υποχρεωτικό
Η πραγματική διεύθυνση e-mail του πληρωτή: RFC 5322, έως 254 χαρακτήρες, με τομέα που περιέχει τελεία. Αποθηκεύεται με πεζά γράμματα.
Ο πραγματικός αριθμός τηλεφώνου του πληρωτή σε διεθνή μορφή (+ κωδικός χώρας, E.164), για παράδειγμα +37061234567. Κανόνας για την Τουρκία: +90 και ο 10ψήφιος εθνικός αριθμός (5xx κινητό, 2xx, 3xx, 4xx σταθερό, 8xx ειδικοί αριθμοί). Αριθμός που στέλνεται χωρίς κωδικό χώρας αντιμετωπίζεται ως τουρκικός (+90).
+37061234567
card
object
Υπό προϋποθέσεις
Τα στοιχεία κάρτας που εισήγαγε ο πληρωτής (δείτε τον πίνακα παρακάτω). Στείλτε είτε card είτε bin6, όχι και τα δύο.
{ … }
bin6
string
Υπό προϋποθέσεις
Μόνο για BIN τύπου hosted: τα πρώτα έξι ψηφία. Ο πληρωτής εισάγει τα στοιχεία της κάρτας στη σελίδα πληρωμής. Αίτημα για BIN τύπου direct μόνο με bin6 επιστρέφει 4090055230.
540061
card.number
string
Υποχρεωτικό
13–19 ψηφία χωρίς κενά, με έγκυρο ψηφίο ελέγχου (15 ψηφία για American Express).
4355090000044242
card.holder_name
string
Υποχρεωτικό
Το ονοματεπώνυμο όπως αναγράφεται στην κάρτα, 2–120 χαρακτήρες.
JONAS KAZLAUSKAS
card.expiry_month
string
Υποχρεωτικό
Δύο ψηφία, από 01 έως 12.
12
card.expiry_year
string
Υποχρεωτικό
Δύο ψηφία, για παράδειγμα 30. Η κάρτα δεν πρέπει να έχει λήξει και το έτος μπορεί να είναι το πολύ 30 χρόνια στο μέλλον.
30
card.cvv
string
Υποχρεωτικό
Ακριβώς 3 ψηφία, ή 4 ψηφία για American Express. Ποτέ μην το αποθηκεύετε ούτε να το καταγράφετε.
123
Όλες οι τιμές της κάρτας είναι συμβολοσειρές. Τα άγνωστα πεδία μέσα στο card απορρίπτονται.
Για hosted BIN, η σελίδα πληρωμής δεν ζητά ξανά από τον πληρωτή τη διεύθυνση 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 ή τον αριθμό τηλεφώνου σας) και αφήστε τον να διορθώσει το πεδίο πριν δημιουργήσετε ξανά την πληρωμή. Το Direct API επιστρέφει τα μηνύματα του errors μεταφρασμένα· ο πίνακας δείχνει το αγγλικό κείμενο.
Οι σελίδες συνδέσμων πληρωμής της Payniko εφαρμόζουν τους ίδιους κανόνες στη διεύθυνση e-mail και στον αριθμό τηλεφώνου που εισάγει εκεί ο πληρωτής και του δείχνουν την αιτία της απόρριψης.
Ανακατευθύνετε το πρόγραμμα περιήγησης στο next_action.url (3-D Secure ή σελίδα πληρωμής για BIN τύπου hosted).
processing
Περιμένετε την ειδοποίηση webhook και μη δημιουργήσετε νέα πληρωμή.
succeeded
Εκτελέστε την παραγγελία.
failed
Εμφανίστε το failure.message και ζητήστε από τον πληρωτή άλλη κάρτα.
Η κλήση μπορεί να διαρκέσει αρκετά δευτερόλεπτα όσο η Payniko επικοινωνεί με την τράπεζα. Χρησιμοποιήστε χρονικό όριο τουλάχιστον 60 δευτερολέπτων και, μετά από λήξη του, στείλτε ξανά το αίτημα με το ίδιο Idempotency-Key και σώμα. Όσο το πρώτο αίτημα εκτελείται ακόμη (έως 3 λεπτά), η επανάληψη επιστρέφει 4090055503 με metadata.retry_class: retry_same_request: περιμένετε λίγα δευτερόλεπτα και στείλτε ξανά το ίδιο αίτημα.
Για BIN τύπου hosted, τυχόν card που αποστέλλεται αγνοείται και ο πληρωτής συνεχίζει στη σελίδα πληρωμής.
Αν υπάρχει ήδη πληρωμή για το merchant_order_id ή αν το ίδιο Idempotency-Key αποσταλεί με διαφορετικό σώμα, το αίτημα απορρίπτεται με 4090055503.
Τα μη έγκυρα στοιχεία κάρτας απορρίπτονται με 4090055234. Το metadata.developer_detail προσδιορίζει το πεδίο: card_number_invalid, card_expiry_invalid ή card_cvv_invalid. Ζητήστε από τον πληρωτή να εισαγάγει ξανά τα στοιχεία της κάρτας.
Στοιχεία επικοινωνίας που λείπουν ή δεν είναι έγκυρα απορρίπτονται με 4221000001· το errors αναφέρει customer, customer.email ή customer.phone.
3. 3-D Secure και πρόγραμμα περιήγησης
Όταν η κατάσταση είναι requires_action, το πρόγραμμα περιήγησης του πληρωτή πρέπει να ανοίξει το next_action.url, όπου η τράπεζα εκτελεί τον έλεγχο 3-D Secure ή ο πληρωτής συνεχίζει στη σελίδα πληρωμής.
Ανακατευθύνετε ολόκληρη τη σελίδα στο next_action.url ή ανοίξτε το σε νέα καρτέλα· και οι δύο μέθοδοι υποστηρίζονται πάντα.
Η σελίδα δεν ζητά ξανά τα στοιχεία της κάρτας· συλλέγει μόνο τα δεδομένα του προγράμματος περιήγησης που απαιτεί η τράπεζα.
Χρησιμοποιείτε πάντα το πιο πρόσφατο next_action.url πριν από το next_action.expires_at του (το τέλος του βήματος 3-D Secure). Μην ανοίγετε ποτέ ξανά προηγούμενο URL.
Στη συνέχεια, το πρόγραμμα περιήγησης ανακατευθύνεται στο success_url (μόνο σε succeeded) ή στο fail_url με υπογεγραμμένες παραμέτρους (δείτε URL επιστροφής). Έπειτα, ανακτήστε την πληρωμή.
Ενσωμάτωση σε πλαίσιο
Η σελίδα 3-D Secure δεν μπορεί να εμφανιστεί μέσα σε πλαίσιο· ανακατευθύνετε ολόκληρη τη σελίδα ή ανοίξτε νέα καρτέλα. Μόνο η σελίδα πληρωμής ενός BIN τύπου hosted επιτρέπεται να ενσωματωθεί σε πλαίσιο, μόνο με κλειδιά παραγωγής και μόνο στην ακριβή προέλευση (origin) του success_url της πληρωμής. Η σελίδα πληρωμής του Hosted API δεν μπορεί ποτέ να ενσωματωθεί σε πλαίσιο.
Η σελίδα πληρωμής ενός BIN τύπου hosted ενσωματώνει την ασφαλή οθόνη πληρωμής και 3-D Secure του ίδιου του παρόχου πληρωμών και μπορεί να εμφανιστεί ως iframe μέσα στη δική σας φόρμα πληρωμής.
Αν ενσωματώσετε σε πλαίσιο τη σελίδα πληρωμής ενός BIN τύπου hosted, τα μηνύματα που αποστέλλει στο παράθυρό σας χρησιμεύουν μόνο ως σήμα για να ελέγξετε την πληρωμή. Αποδεχτείτε τα μόνο από την προέλευση του next_action.url και από το δικό σας πλαίσιο, αγνοήστε το περιεχόμενό τους και αναθέστε την ανάκτηση της πληρωμής στον διακομιστή σας.
Πρόγραμμα περιήγησης
// Only if you show the payment page of a hosted BIN in a frame on the exact origin of success_url.
// nextActionUrl: next_action.url from your server.
const pageOrigin = new URL(nextActionUrl).origin;
window.addEventListener('message', (event) => {
if (event.origin !== pageOrigin || event.source !== frame.contentWindow) return;
// A wake-up call only: ignore event.data. Ask your server to read the payment.
fetch('/checkout/ORDER-2026-002/refresh', {method: 'POST', credentials: 'same-origin'});
});
Αντικείμενο πληρωμής
Οι κλήσεις δημιουργίας και ανάκτησης επιστρέφουν αυτό το αντικείμενο μέσα στο data, και κάθε ειδοποίηση webhook πληρωμής το περιέχει. Η δομή του είναι ίδια στο Hosted API και στο Direct API.
Πεδίο
Τύπος
Περιγραφή
payment_id
string · LUVEI-ABCD-123456
Το αναγνωριστικό της πληρωμής στο Payniko . Αποθηκεύστε το μαζί με την παραγγελία σας.
merchant_order_id
string
Ο κωδικός της παραγγελίας σας, ακριβώς όπως υποβλήθηκε.
flow
string
hosted για το Hosted API, direct για το Direct API.
hosteddirect
status
string
Η τρέχουσα κατάσταση της πληρωμής· δείτε τον πίνακα καταστάσεων παρακάτω.
Το νόμισμα του ποσού· το κύριο νόμισμα του συστήματος, αν δεν ορίστηκε νόμισμα.
environment
string
Το περιβάλλον του κλειδιού με το οποίο δημιουργήθηκε η πληρωμή.
sandboxlive
next_action
object | null
Η επόμενη ενέργεια που απαιτείται από τον πληρωτή. Ορίζεται μόνο όσο το status είναι requires_action.
next_action.type
string
Πάντα redirect.
next_action.url
string · uri
Ανοίξτε αυτό το URL στο πρόγραμμα περιήγησης του πληρωτή ακριβώς όπως επιστρέφεται. Μην το κατασκευάζετε και μην το τροποποιείτε ποτέ.
next_action.expires_at
string · date-time
Προθεσμία χρήσης του URL. Στο Direct API, αντιστοιχεί στο τέλος του βήματος 3-D Secure και μπορεί να είναι νωρίτερα από το expires_at της ίδιας της πληρωμής· στο Hosted API, είναι η προθεσμία της πληρωμής. Μόλις παρέλθει, η πληρωμή μεταβαίνει στην επόμενη κατάστασή της και το next_action γίνεται null.
Η αιτία της αποτυχίας. Ορίζεται μόνο όταν το status είναι failed.
failure.code
string
Κωδικός αιτίας για χρήση στη λογική της εφαρμογής σας· δείτε τη λίστα παρακάτω.
failure.message
string
Σύντομο μήνυμα κατάλληλο για εμφάνιση στον πληρωτή ως απλό κείμενο.
created_at
string · date-time
Χρόνος δημιουργίας της πληρωμής.
expires_at
string · date-time
Προθεσμία ολοκλήρωσης της πληρωμής.
completed_at
string · date-time | null
Χρόνος κατά τον οποίο η πληρωμή περιήλθε σε τελική κατάσταση.
Το next_action μπορεί για σύντομο διάστημα να είναι null ακόμη και σε requires_action· σε αυτή την περίπτωση, ανακτήστε ξανά την πληρωμή. Ο πλήρης αριθμός κάρτας, το CVV, η ημερομηνία λήξης και το όνομα του κατόχου δεν επιστρέφονται ποτέ. Τα card.network και card.bank μπορεί να είναι null όταν δεν είναι γνωστά.
Τα στοιχεία customer που στάλθηκαν κατά τη δημιουργία της πληρωμής δεν αποτελούν μέρος αυτού του αντικειμένου και δεν εμφανίζονται στα webhooks.
Το πρόγραμμα περιήγησης πρέπει να ανοίξει το next_action.url για το 3-D Secure ή για τη σελίδα πληρωμής ενός BIN τύπου hosted.
Ανακατευθύνετε τον πληρωτή στο next_action.url.
processing
Η απάντηση της τράπεζας εκκρεμεί. Αυτή η κατάσταση μπορεί επίσης να επιστραφεί απευθείας στην απάντηση δημιουργίας.
Περιμένετε το webhook και μη δημιουργήσετε νέα πληρωμή. Αν η τράπεζα δεν απαντήσει ποτέ, η πληρωμή μπορεί να παραμείνει σε processing μέχρι τη συμφωνία και στη συνέχεια καταλήγει σε failed ή expired.
succeeded
Τα κεφάλαια έχουν εισπραχθεί.
Εκτελέστε την παραγγελία.
failed
Η πληρωμή δεν ολοκληρώθηκε. Το failure περιέχει την αιτία.
Εμφανίστε το failure.message. Για νέα προσπάθεια, δημιουργήστε νέα πληρωμή με νέο merchant_order_id.
expired
Ο πληρωτής δεν ολοκλήρωσε την πληρωμή πριν από το expires_at.
Αν ο πληρωτής εξακολουθεί να επιθυμεί να πληρώσει, δημιουργήστε νέα πληρωμή.
cancelled
Η πληρωμή ακυρώθηκε.
Μην εκτελέσετε την παραγγελία.
Σενάριο
Κατάσταση
Συμβάν
Έγκριση μετά από 3-D Secure
requires_action → succeeded
payment.succeeded
Απόρριψη
failed
payment.failed
Εκκρεμεί η απάντηση της τράπεζας
processing
Κανένα έως το τελικό αποτέλεσμα
Καμία απάντηση από την τράπεζα· λήξη χρονικού ορίου της προσπάθειας
processing → failed / expired
payment.failed
Ο πληρωτής δεν ξεκίνησε την πληρωμή και η προθεσμία παρήλθε (αν ο πληρωτής εγκαταλείψει την πληρωμή στο μέσο της διαδικασίας, μπορεί να καταλήξει σε failed)
expired
payment.failed
Αντιμετωπίστε κάθε κατάσταση που δεν αναφέρεται εδώ ως 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.
URL επιστροφής
Κατά την επιστροφή του πληρωτή, το πρόγραμμα περιήγησης ανακατευθύνεται στο success_url μόνο αν η πληρωμή είναι succeeded και στο fail_url σε κάθε άλλη περίπτωση. Στο URL προστίθενται υπογεγραμμένες παράμετροι. Επαληθεύστε τις για να εμφανίσετε τη σωστή σελίδα και εκτελέστε την παραγγελία μόνο αφού η ειδοποίηση webhook ή ένα GET επιβεβαιώσει την κατάσταση succeeded.
Direct API: αν το αποτέλεσμα δεν είναι ακόμη τελικό όταν ο πληρωτής επιστρέφει από το 3-D Secure, το πρόγραμμα περιήγησης δεν ανακατευθύνεται στο fail_url· ο πληρωτής παραμένει σε μια ουδέτερη σελίδα αποτελέσματος της Payniko. Βασιστείτε στο webhook ή σε ένα GET για το αποτέλεσμα.
Πεδίο
Περιγραφή
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 για το επόμενο κ.ο.κ. Η τιμή δεν αλλάζει ποτέ για μια δεδομένη πληρωμή.
sig
HMAC-SHA256 σε δεκαεξαδική μορφή με πεζά, 64 χαρακτήρες.
Συμβολοσειρά προς υπογραφή
ts + "." + payment_id + "." + status + "." + failure_code // failure_code empty: keep the last dot
Επιλέξτε το μυστικό κλειδί που αντιστοιχεί στο κλειδί το οποίο προσδιορίζει το key_version. Πρόκειται για το κλειδί με το οποίο δημιουργήθηκε η πληρωμή, ακόμη και αν τα κλειδιά έχουν αντικατασταθεί στο μεταξύ.
Συνθέστε τη συμβολοσειρά από τις τιμές μετά την αποκωδικοποίηση URL. Αν δεν υπάρχει failure_code, διατηρήστε την τελική τελεία.
Υπολογίστε το HMAC-SHA256 και συγκρίνετέ το με το sig σε σταθερό χρόνο. Βεβαιωθείτε ότι το payment_id αντιστοιχεί σε μία από τις πληρωμές σας.
Ανακτήστε την πληρωμή με 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_id
string · path
Υποχρεωτικό
Το payment_id που επιστράφηκε κατά τη δημιουργία της πληρωμής. Οποιοδήποτε κλειδί του ίδιου περιβάλλοντος μπορεί να την ανακτήσει.
./payniko.sh GET '/api/payments/3c2d7a4e-5b1f-4e8a-9d06-7f2b1c9e4a35'
Επαληθεύετε κάθε ειδοποίηση webhook: το HMAC-SHA256 του X-Timestamp + "." + raw body, υπολογισμένο με το μυστικό κλειδί που αντιστοιχεί στο κλειδί API με το οποίο δημιουργήθηκε η πληρωμή, πρέπει να ισούται με το X-Signature και το X-Timestamp πρέπει να βρίσκεται εντός 300 δευτερολέπτων. Αποθηκεύστε κάθε event_id μία φορά και απαντήστε άμεσα με 2xx. Για παραδείγματα κώδικα και τη συμπεριφορά των επαναλήψεων, δείτε Ειδοποιήσεις webhook.
Σφάλματα
Κωδικός
HTTP
Σημασία
Συνιστώμενη ενέργεια
1000001
422
Το αίτημα δεν είναι έγκυρο: άγνωστο πεδίο, μη έγκυρη μορφή, συμβολοσειρά ερωτήματος σε POST, παράμετρος ερωτήματος που δεν υποστηρίζεται ή σώμα που δεν είναι αντικείμενο JSON. Το errors παραθέτει τα πεδία που αφορά το σφάλμα.
Διορθώστε τα πεδία που αναφέρονται.
1000009
422
Η κεφαλίδα Idempotency-Key λείπει ή δεν είναι έγκυρη.
Σε κάθε POST στείλτε κλειδί 8–128 χαρακτήρων από το σύνολο A-Z a-z 0-9 _ -.
0055502
401 / 403
Η πρόσβαση απορρίφθηκε. 401: λείπει η κεφαλίδα X-Api-Key ή X-Merchant-Code, μια κεφαλίδα έχει σταλεί δύο φορές ή έχει μη έγκυρη μορφή, το κλειδί είναι άγνωστο ή έχει ανακληθεί, ο κωδικός εμπόρου δεν αντιστοιχεί ή στάλθηκε X-Environment που δεν αντιστοιχεί στο κλειδί. 403: ο λογαριασμός ή η πρόσβαση στο Direct API δεν είναι ενεργή.
Επαληθεύστε τις κεφαλίδες και το κλειδί API. Μην επαναλαμβάνετε το αίτημα αυτόματα.
0055503
409
Σύγκρουση στο Direct API: υπάρχει ήδη πληρωμή για αυτό το merchant_order_id, το Idempotency-Key χρησιμοποιήθηκε με διαφορετικό σώμα ή για πληρωμή Hosted API, ή το αρχικό αίτημα βρίσκεται ακόμη σε εξέλιξη.
Ανακτήστε την πληρωμή με GET. Στείλτε ξανά μόνο πανομοιότυπο αίτημα.
0055210
422
Το BIN δεν αποτελείται από έξι ψηφία.
Στείλτε τα πρώτα έξι ψηφία του αριθμού κάρτας.
0055234
409
Τα στοιχεία της κάρτας δεν είναι έγκυρα: αριθμός κάρτας, ημερομηνία λήξης ή CVV. Το metadata.developer_detail προσδιορίζει το πεδίο: card_number_invalid, card_expiry_invalid ή card_cvv_invalid.
Ζητήστε από τον πληρωτή να εισαγάγει ξανά τα στοιχεία της κάρτας.
0055233
409
Το BIN δεν είναι γνωστό.
Ζητήστε από τον πληρωτή άλλη κάρτα. Δεν πρόκειται για απόρριψη από την τράπεζα.
0055230
409
Αυτή η κάρτα δεν μπορεί να χρησιμοποιηθεί για αυτή την πληρωμή.
Ζητήστε από τον πληρωτή άλλη κάρτα. Δεν πρόκειται για απόρριψη από την τράπεζα.
0055231
409
Αυτή η κάρτα δεν γίνεται δεκτή για αυτή την πληρωμή.
Ζητήστε από τον πληρωτή άλλη κάρτα.
0055221
409
Έχει συμπληρωθεί ένα όριο αυτής της κάρτας.
Χρησιμοποιήστε άλλη κάρτα ή επαναλάβετε το αίτημα αργότερα.
0055235
422
Το ποσό δεν γίνεται δεκτό (Direct API και έλεγχος BIN). Το amount_limit περιέχει τα min και max, όταν είναι διαθέσιμα.
Προσαρμόστε το ποσό.
0055001
409
Ο λογαριασμός σας δεν είναι έτοιμος για αυτή την πληρωμή. Με κλειδί sandbox, αυτό σημαίνει επίσης ότι η προσομοίωση sandbox δεν είναι προσωρινά διαθέσιμη (developer_detail: sandbox_unavailable), ισοδύναμο με 9000013.
Επικοινωνήστε με την υποστήριξη.
1000002
404
Δεν βρέθηκε: η διαδρομή είναι άγνωστη ή αυτό το προϊόν API δεν είναι ενεργοποιημένο στην πλατφόρμα. Ένα API απενεργοποιημένο για τον λογαριασμό σας επιστρέφει αντ' αυτού 403 (9000040 στο Hosted API, 0055502 στο Direct API).
Επαληθεύστε τη διαδρομή· επικοινωνήστε με την υποστήριξη αν το API θα έπρεπε να είναι διαθέσιμο.
1000011
503
Η υπηρεσία δεν είναι προσωρινά διαθέσιμη.
Επαναλάβετε το ίδιο αίτημα. Ανακτήστε τους υπάρχοντες πόρους πριν δημιουργήσετε νέους.
Τα σφάλματα του Direct API περιλαμβάνουν επίσης το metadata.retry_class (no_retry, retry_same_request, retry_after, read_order). Ένα κλειστό παράθυρο του προγράμματος περιήγησης ή μια λήξη χρονικού ορίου από την πλευρά σας δεν σημαίνει ότι η πληρωμή απέτυχε· ανακτήστε την πληρωμή. Για τον πλήρη κατάλογο, δείτε Κωδικοί σφαλμάτων.
Δοκιμές με κάρτες sandbox
Με κλειδί sandbox, ο έλεγχος BIN και η πληρωμή χρησιμοποιούν σταθερά δοκιμαστικά δεδομένα: τα 435509 και 400000 είναι BIN τύπου direct και το 540061 είναι BIN τύπου hosted. Χρησιμοποιήστε οποιαδήποτε μελλοντική ημερομηνία λήξης και οποιοδήποτε τριψήφιο 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.
4355090000089999
Hosted API: processing χωρίς βήμα 3-D Secure. Direct API: δοκιμαστικό βήμα 3-D Secure και στη συνέχεια processing.
Με το Direct API, μια απορριφθείσα δοκιμαστική κάρτα επιστρέφει failed απευθείας στην απάντηση δημιουργίας· ενδιάμεσο requires_action εμφανίζεται μόνο στη σελίδα πληρωμής ενός hosted BIN. Βασίστε τις αποφάσεις σας στο webhook ή σε ένα GET.
Ολοκληρωμένο παράδειγμα
Ένας ελάχιστος διακομιστής Node.js που καλύπτει τον έλεγχο BIN, τη δημιουργία πληρωμής, την ανακατεύθυνση για 3-D Secure, το URL επιστροφής και την ειδοποίηση webhook. Χρησιμοποιεί τη βοηθητική συνάρτηση αιτημάτων και τις συναρτήσεις επαλήθευσης από τις ενότητες Ειδοποιήσεις webhook και URL επιστροφής.
server.mjs
// server.mjs: Direct 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); };
const reply = (res, data) => {
res.writeHead(200, {'Content-Type': 'application/json'});
res.end(JSON.stringify(data.status === 'requires_action' && data.next_action
? {redirect: data.next_action.url} : {status: data.status, failure: data.failure}));
};
http.createServer(async (req, res) => {
const url = new URL(req.url, site);
// 1. BIN check, 2. create the payment. Your own HTTPS form posts the card here.
if (req.method === 'POST' && url.pathname === '/pay') {
const form = JSON.parse((await readBody(req)).toString('utf8'));
const bin6 = form.number.slice(0, 6);
let mode = null;
try {
mode = (await api('GET', `/api/payments/bin/${bin6}?amount=100.00¤cy=USD`)).data.payment_mode;
} catch (error) {
if (error.status === undefined || error.status >= 500 || error.status === 429) throw error;
}
if (mode !== 'direct' && mode !== 'hosted') { res.writeHead(422).end('Please use another card.'); return; }
const orderId = 'ORDER-' + Date.now();
const key = randomUUID();
orders.set(orderId, {key, status: 'new'});
const body = {
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`,
};
if (mode === 'direct') {
body.card = {number: form.number, holder_name: form.holder_name,
expiry_month: form.expiry_month, expiry_year: form.expiry_year, cvv: form.cvv};
} else {
body.bin6 = bin6; // payer enters the card on the payment page
}
const {data} = await api('POST', '/api/payments/direct', body, key);
Object.assign(orders.get(orderId), {paymentId: data.payment_id, status: data.status});
reply(res, data); // 3. browser goes to next_action.url
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);