Download the PHP package tcgunel/omnipay-paynkolay without Composer
On this page you can find all versions of the php package tcgunel/omnipay-paynkolay. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Informations about the package omnipay-paynkolay
Omnipay: PayNKolay
Paynkolay payment gateway driver for the Omnipay PHP payment processing library.
Omnipay is a framework-agnostic, multi-gateway payment processing library for PHP. This package implements Paynkolay support for Omnipay, including direct (3DS) payment, hosted-page Pay By Link, post-order send-link, cancel/refund, transaction listing, installment lookup, and the postback-verification handshake.
The package is built against Paynkolay's documentation at https://paynkolay.com.tr/entegrasyon/ — but it also absorbs the divergences between the published docs and the actual production payloads (notably the postback signature uses a completely different algorithm than every other endpoint). See the Doc-vs-prod quirks section.
Installation
Requires PHP 8.3+ and omnipay/common ^3.0.
Credentials
A merchant needs four values from Paynkolay's portal:
| Paynkolay portal label | Setter | Used as sx on |
|---|---|---|
| SX Token | setSxToken() |
Purchase, CompletePurchase, MerchantInfo, BinInstallment, Pay By Link |
| SX List Token | setSxListToken() |
PaymentList |
| SX İptal Token | setSxCancelToken() |
Cancel, Refund |
| Merchant Secret Key | setMerchantSecretKey() |
HMAC secret — signs every outgoing hashDatav2 and verifies the inbound postback |
All four values are emitted by Paynkolay during merchant onboarding (Test Ortamı Bilgileri for sandbox, Canlı Ortam Bilgileri for production). Paynkolay's portal sometimes labels the secret key in older docs as "Store Key" — same thing, just an older name.
Gateway setup
Endpoints:
| Sandbox | Production | |
|---|---|---|
| Base URL | https://paynkolaytest.nkolayislem.com.tr |
https://paynkolay.nkolayislem.com.tr |
Methods
| Method | Endpoint | Returns |
|---|---|---|
purchase() |
POST /Vpos/v1/Payment |
PurchaseResponse (3DS-aware) |
completePurchase() |
POST /Vpos/v1/CompletePayment |
CompletePurchaseResponse |
cancel() |
POST /Vpos/v1/CancelRefundPayment (type=cancel) |
CancelResponse |
refund() |
POST /Vpos/v1/CancelRefundPayment (type=refund) |
RefundResponse |
binInstallment() |
POST /Vpos/Payment/PaymentInstallments |
BinInstallmentResponse |
merchantInfo() |
POST /Vpos/Payment/GetMerchandInformation |
MerchantInfoResponse (full installment table) |
paymentList() |
POST /Vpos/Payment/PaymentList |
PaymentListResponse (transaction history) |
payByLink() |
POST /Vpos/by-link-create |
PayByLinkResponse (redirect URL) |
payByLinkSend() |
POST /Vpos/pay-by-link-create |
PayByLinkSendResponse (issue+deliver) |
payByLinkDelete() |
POST /Vpos/by-link-url-remove |
PayByLinkDeleteResponse |
acceptNotification() |
n/a | Notification (parses + verifies the 3DS postback POST) |
Direct (non-3D) payment
3D Secure payment
Step 1 — initiate
Step 2 — handle the postback
After the user authenticates with the bank, Paynkolay POSTs to returnUrl with the 3DS verification payload. Use acceptNotification() to parse it, verify the hash before trusting any field, and only then call completePurchase():
Notification exposes:
| Method | What it returns |
|---|---|
isSuccessful() |
true only when RESPONSE_CODE === 2 AND AUTH_CODE is non-empty and not "0" |
getTransactionStatus() |
Omnipay constant: STATUS_COMPLETED or STATUS_FAILED |
getMessage() |
RESPONSE_DATA |
getTransactionId() |
the CLIENT_REFERENCE_CODE you sent |
getTransactionReference() |
Paynkolay's REFERENCE_CODE |
getCode() |
raw RESPONSE_CODE |
verifyHash($merchantSecretKey) |
constant-time check of the postback hashData field |
getData() |
raw POST array |
⚠️ Always call
verifyHash()before trusting the rest of the postback. Without it, anyone who knows the merchant's reference code could POST a fakeRESPONSE_CODE=2to your callback URL.Paynkolay's merchant panel has an "AutoComplete" toggle. If it's off, you must call
completePurchase()after verifying. If it's on, Paynkolay finalises the charge itself —completePurchase()becomes a no-op but you should still call it for idempotency.
Cancel (same-day reversal)
type=cancel, only valid for transactions made the same day. amount and trxDate are both required by the gateway.
Note: trxDate must be in YYYY.MM.DD format on the wire (e.g. 2026.01.15).
Refund (subsequent days)
type=refund. Same endpoint as cancel, the gateway switches behaviour by type.
Transaction listing
Uses the dedicated SX List Token. Date format is DD.MM.YYYY (note: different from Cancel/Refund's YYYY.MM.DD — Paynkolay's date formatting is endpoint-specific).
The response shape isn't formally documented and Paynkolay's Postman collection ships no saved example. PaymentListResponse therefore discovers the transaction list adaptively — it picks the first top-level value that's a list of associative arrays — so you can wire this up against production without needing a package patch when actual response field names turn out different from your test fixture.
Pay By Link — checkout redirect
"Ortak Ödeme Sayfası". Creates a hosted-payment URL the customer should be redirected to. Same hash format as direct purchase.
After payment, the customer is returned to returnUrl and the postback flow is identical to direct 3DS — use acceptNotification() + verifyHash() + completePurchase().
Pay By Link — send to customer
For the case where the order already exists and staff issues a payment link by SMS/email (e.g. an invoice). Endpoint and field naming are uniquely inconsistent with the rest of the gateway — see the quirks section.
To invalidate a previously issued link:
BIN installment lookup
Merchant installment table
Doc-vs-prod quirks the package absorbs
Paynkolay's published docs at https://paynkolay.com.tr/entegrasyon/ lag the gateway's actual behaviour in several places. The package handles all of these — you should not need to special-case them in your application code, but they're documented here so future-you knows what to expect when reading raw payloads.
Postback signature (hashData) uses a completely different algorithm than outgoing requests.
This is the biggest gotcha and the one the package most aggressively absorbs.
Outgoing requests (hashDatav2) |
Inbound postback (hashData) |
|
|---|---|---|
| Field separator | \| |
none (raw concatenation) |
| Hash algorithm | SHA-512 → base64 | SHA-1 → hex-decode → base64 |
| Field name | hashDatav2 |
hashData |
Postback field list, in order:
MERCHANT_NO + REFERENCE_CODE + AUTH_CODE + RESPONSE_CODE + USE_3D + RND + INSTALLMENT + AUTHORIZATION_AMOUNT + MERCHANT_SECRET_KEY
The algorithm was reverse-engineered from the official WooCommerce reference plugin — the docs page doesn't spell it out. PayNKolayHelper::verifyPostbackHash() and Notification::verifyHash() encapsulate it.
Other wire-format inconsistencies
| Endpoint | Quirk | Production wants |
|---|---|---|
/Vpos/by-link-create (Pay By Link) |
Docs imply currencyNumber like the direct API uses |
gateway requires currencyCode (same numeric value; only the field name differs) |
/Vpos/pay-by-link-create (Send Link) |
Docs use sx like every other endpoint |
gateway requires the field be renamed TOKEN; every other field is SCREAMING_SNAKE_CASE; hash is still computed against the original sx token |
/Vpos/Payment/PaymentList |
Documented date format unclear | DD.MM.YYYY |
/Vpos/v1/CancelRefundPayment |
Documented date format unclear | YYYY.MM.DD (different from PaymentList!) |
/Vpos/v1/CompletePayment |
docs imply hashDatav2 is required |
gateway accepts a plain sx + referenceCode (no outgoing hash); postback hash must be verified before this call |
CompletePurchaseResponse on bank decline |
RESPONSE_DATA always populated | sometimes empty — getMessage() falls back to the documented bank-code lookup table |
Bank decline codes
Constants/ErrorCodes::MESSAGES is a verbatim transcription of https://paynkolay.com.tr/entegrasyon/43-error-codes.php (as of 2026-05-20). getMessage() on the relevant responses uses it as fallback when RESPONSE_DATA is empty. ErrorCodes::message($code) is public if you want to translate codes yourself; it pads single-digit codes to two characters (the gateway emits "0" but the table is keyed on "00").
Testing
The suite runs against mocked HTTP responses; no network access required.
License
MIT.