All API requests must be authenticated using API Key and API Secret headers.
| Header Name | Description |
|---|---|
| X-API-KEY | Your API Key |
| X-API-SECRET | Your API Secret |
These credentials are unique to each merchant and must be included in every API request. Requests without valid credentials will receive a 401 Unauthorized response. You can get credentials from Settings page in our dashboard.
curl -X POST https://api.example.com/api/payment \
-H "Content-Type: application/json" \
-H "X-API-KEY: your_api_key_here" \
-H "X-API-SECRET: your_api_secret_here" \
-d '{"orderId": "ORD-12345", ...}'
Ensure that your API Key and API Secret are kept secure and not shared publicly.
Endpoint: POST /api/payment
Description: Process a payment transaction
{
"orderId": "string",
"amount": "number",
"currency": "string",
"cardHolderName": "string",
"pan": "string",
"expiryMonth": "string",
"expiryYear": "string",
"cvv": "string",
"requestIp": "string",
"requestPort": "number",
"customerId": "string"
}
| Field | Description | Example |
|---|---|---|
| orderId | Client's tracking number for the order | ORD-12345 |
| amount | Transaction amount | 100.50 |
| currency | Currency code | TRY |
| cardHolderName | Name of the card holder | John Doe |
| pan | Primary Account Number (card number) | 5421190122090656 |
| expiryMonth | Card expiry month (2 digits) | 04 |
| expiryYear | Card expiry year (2 digits) | 28 |
| cvv | Card Verification Value | 916 |
| requestIp | IP address of the client making the request | 192.168.1.1 |
| requestPort | Port number of the client making the request | 8080 |
| customerId | Optional unique identifier for the customer | CUST-12345 |
Returns an ApiPaymentResponse object with payment details.
{
"paymentId": "UUID",
"orderId": "string",
"amount": "number",
"installmentCount": "number",
"currency": "string",
"merchantCommission": "number",
"status": "string",
"transactionType": "string",
"paymentDate": "date",
"cardHolderName": "string",
"pan": "string",
"domInt": "string",
"cardScheme": "string",
"cardType": "string",
"cardSubType": "string",
"loyaltyCode": "string",
"externalTransactionId": "string",
"authCode": "string",
"resultCode": "string",
"resultMessage": "string",
"customerId": "string"
}
| Field | Description | Example |
|---|---|---|
| paymentId | Unique identifier for the payment | 123e4567-e89b-12d3-a456-426614174000 |
| orderId | Client's tracking number for the order | ORD-12345 |
| amount | Transaction amount | 100.50 |
| installmentCount | Number of installments | 3 |
| currency | Currency code | TRY |
| merchantCommission | Commission charged to the merchant | 2.50 |
| status | Payment status (SUCCESS, FAILED, ENROLLED, PENDING_APPROVAL, CANCELLED, CAPTURED, REJECTED) | SUCCESS |
| transactionType | Transaction type (SALE, REFUND) | SALE |
| paymentDate | Date and time of the payment | 2023-05-01T14:30:00Z |
| cardHolderName | Name of the card holder | John Doe |
| pan | Masked Primary Account Number | 411111******1111 |
| domInt | Domestic or International transaction | DOM |
| cardScheme | Card scheme (e.g., VISA, MASTERCARD) | VISA |
| cardType | Type of card (e.g., CREDIT, DEBIT) | CREDIT |
| loyaltyCode | Loyalty program code (if applicable) | GOLD123 |
| externalTransactionId | Transaction ID from the payment provider | EXT123456789 |
| authCode | Authorization code from the payment provider | AUTH987654 |
| resultCode | Result code from the payment provider | SUCCESS |
| resultMessage | Result message from the payment provider | Successful |
| customerId | Unique identifier for the customer (if provided) | CUST-12345 |
Endpoint: POST /api/pre-authorization
Description: Process a pre-authorization transaction
{
"orderId": "string",
"amount": "number",
"currency": "string",
"cardHolderName": "string",
"pan": "string",
"expiryMonth": "string",
"expiryYear": "string",
"cvv": "string",
"requestIp": "string",
"requestPort": "number",
"customerId": "string"
}
| Field | Description | Example |
|---|---|---|
| orderId | Client's tracking number for the order | ORD-12345 |
| amount | Transaction amount | 100.50 |
| currency | Currency code | TRY |
| cardHolderName | Name of the card holder | John Doe |
| pan | Primary Account Number (card number) | 5421190122090656 |
| expiryMonth | Card expiry month (2 digits) | 04 |
| expiryYear | Card expiry year (2 digits) | 28 |
| cvv | Card Verification Value | 916 |
| requestIp | IP address of the client making the request | 192.168.1.1 |
| requestPort | Port number of the client making the request | 8080 |
| customerId | Optional unique identifier for the customer | CUST-12345 |
Returns an ApiPaymentResponse object with pre-authorization details (same as Payment response).
Endpoint: POST /api/pre-authorization/{paymentId}/capture
Description: Capture a previously authorized payment
{
"amount": "number",
"currency": "string"
}
| Field | Description | Example |
|---|---|---|
| amount | Amount to capture (can be less than or equal to the pre-authorized amount) | 90.00 |
| currency | Currency code | TRY |
Returns an ApiPaymentResponse object with capture details (same as Payment response).
Description: Process a 3D Secure payment
Endpoint: POST /api/3ds
{
"orderId": "string",
"amount": "number",
"currency": "string",
"installmentCount": "number",
"interestPaidByCustomer": "boolean",
"cardHolderName": "string",
"pan": "string",
"expiryMonth": "string",
"expiryYear": "string",
"cvv": "string",
"successUrl": "string",
"failureUrl": "string",
"requestIp": "string",
"requestPort": "number",
"customerId": "string"
}
| Field | Description | Example |
|---|---|---|
| orderId | Client's tracking number for the order | ORD-12345 |
| amount | Transaction amount | 100.50 |
| currency | Currency code | TRY |
| installmentCount | Number of installments (optional, default 1). You should check available installment options via Installment Options API before setting this value. | 3 |
| interestPaidByCustomer | Whether interest is paid by customer (optional) | true |
| cardHolderName | Name of the card holder | John Doe |
| pan | Primary Account Number (card number) | 5421190122090656 |
| expiryMonth | Card expiry month (2 digits) | 04 |
| expiryYear | Card expiry year (2 digits) | 28 |
| cvv | Card Verification Value | 916 |
| successUrl | URL to redirect after successful 3D Secure authentication | https://example.com/3ds/success |
| failureUrl | URL to redirect after failed 3D Secure authentication | https://example.com/3ds/failure |
| requestIp | IP address of the client making the request | 192.168.1.1 |
| requestPort | Port number of the client making the request | 8080 |
| customerId | Optional unique identifier for the customer | CUST-12345 |
{
"paymentId": "UUID",
"success": "boolean",
"resultCode": "string",
"resultMessage": "string",
"htmlContent": "string"
}
| Field | Description | Example |
|---|---|---|
| paymentId | Unique identifier for the payment | 123e4567-e89b-12d3-a456-426614174000 |
| success | Indicates if the enrollment was successful | true |
| resultCode | Result code from the 3D Secure enrollment | SUCCESS |
| resultMessage | Result message from the 3D Secure enrollment | Successful |
| htmlContent | HTML content to be presented to the end user for 3D Secure authentication | <form action="https://3ds.example.com" method="POST">...</form> |
Present the returned htmlContent to the end user. This typically involves rendering an iframe or
redirecting the user to complete the 3D Secure authentication.
After the authentication process, the payment provider will call the provided callback URLs (success or failure) with relevant parameters posted as form data.
| Field | Description | Example |
|---|---|---|
| paymentId | Unique identifier for the payment | 123e4567-e89b-12d3-a456-426614174000 |
| securityKey | Security key received from the 3D Secure authentication process | 3DS_AUTH_KEY_123456 |
Endpoint: POST /api/3ds/{paymentId}/complete
{
"securityKey": "string"
}
| Field | Description | Example |
|---|---|---|
| securityKey | Security key received from the 3D Secure authentication process | 3DS_AUTH_KEY_123456 |
Returns an ApiPaymentResponse object with completed 3D Secure payment details (same as Payment response).
htmlContent to the end user for 3D Secure authentication.paymentId and securityKey from the
form data.paymentId and
securityKey.
Endpoint: POST /api/installment-options
Description: Get available installment options for a card
{
"amount": "number",
"currency": "string",
"pan": "string",
"maxInstallmentCount": "number",
"interestPaidByCustomer": "boolean"
}
| Field | Description | Example |
|---|---|---|
| amount | Transaction amount | 1000.00 |
| currency | Currency code | TRY |
| pan | Primary Account Number (card number) | 5421190122090656 |
| maxInstallmentCount | Maximum number of installments to retrieve (optional) | 12 |
| interestPaidByCustomer | Whether interest is paid by customer (optional) | true |
{
"options": [
{
"installmentCount": 2,
"installmentAmount": 510.00,
"currency": "TRY",
"interestAmount": 20.00,
"totalAmount": 1020.00
},
{
"installmentCount": 3,
"installmentAmount": 345.00,
"currency": "TRY",
"interestAmount": 35.00,
"totalAmount": 1035.00
}
]
}
| Field | Description | Example |
|---|---|---|
| installmentCount | Number of installments | 3 |
| installmentAmount | Amount per installment | 345.00 |
| currency | Currency code | TRY |
| interestAmount | Total interest amount | 35.00 |
| totalAmount | Total amount to be paid | 1035.00 |
Description: Create a checkout link and process payment
Endpoint: POST /api/checkout
{
"orderId": "string",
"amount": "number",
"currency": "string",
"description": "string",
"callback": "string",
"customerId": "string",
"maxInstallmentCount": "number",
"interestPaidByCustomer": "boolean"
}
| Field | Description | Example |
|---|---|---|
| orderId | Client's tracking number for the order | ORD-12345 |
| amount | Transaction amount | 100.50 |
| currency | Currency code | TRY |
| description | Description of the checkout | Product purchase |
| callback | URL to be called after payment processing | https://example.com/payment-callback |
| customerId | Optional unique identifier for the customer | CUST-12345 |
| maxInstallmentCount | Maximum number of installments allowed on the checkout page (optional, defaults to 1 — single payment). Installment options will be available based on the card type and merchant configuration. | 6 |
| interestPaidByCustomer | Whether installment interest is paid by the customer (optional, defaults to false) | true |
{
"checkoutId": "UUID",
"redirectUrl": "string"
}
| Field | Description | Example |
|---|---|---|
| checkoutId | Unique identifier for the checkout | 123e4567-e89b-12d3-a456-426614174000 |
| redirectUrl | URL to redirect the user for payment | https://api.example.com/checkout-link/123e4567-e89b-12d3-a456-426614174000 |
After receiving the response, redirect the user to the redirectUrl to complete the payment.
After the payment is processed, the callback URL provided in the checkout creation request will be called
with the checkoutId as form data.
Endpoint: GET /api/checkout/{checkoutId}
Returns an ApiPaymentResponse object with payment details (same as Payment response).
redirectUrl.checkoutId from the form data.checkoutId.
// Create checkout link
const createCheckout = async (orderDetails) => {
const response = await fetch('https://api.example.com/api/checkout', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-KEY': 'your_api_key_here',
'X-API-SECRET': 'your_api_secret_here'
},
body: JSON.stringify(orderDetails)
});
const data = await response.json();
return data.redirectUrl;
};
// Callback handler
app.post('/payment-callback', async (req, res) => {
const checkoutId = req.body.checkoutId;
const checkoutStatus = await getCheckoutStatus(checkoutId);
if (checkoutStatus.status === 'SUCCESS') {
// Process successful payment
console.log('Payment successful');
} else {
// Handle failed payment
console.log('Payment failed');
}
res.sendStatus(200);
});
// Get checkout status
const getCheckoutStatus = async (checkoutId) => {
const response = await fetch(`https://api.example.com/api/checkout/${checkoutId}`, {
method: 'GET',
headers: {
'X-API-KEY': 'your_api_key_here',
'X-API-SECRET': 'your_api_secret_here'
}
});
return await response.json();
};
Description: Query the current status of a payment by its ID. Use this endpoint to verify payment status independently — especially useful as a fallback when a webhook notification was not received.
Endpoint: GET /api/payment/{paymentId}
Returns an ApiPaymentResponse object. Same structure as payment responses.
paymentId returned from POST /api/3ds.
const getPaymentStatus = async (paymentId) => {
const response = await fetch(`https://api.example.com/api/payment/${paymentId}`, {
method: 'GET',
headers: {
'X-API-KEY': 'your_api_key_here',
'X-API-SECRET': 'your_api_secret_here'
}
});
return await response.json();
};
// 3DS fallback — kullanıcı callback'e yönlendirilmediyse
const paymentId = enrollmentResponse.paymentId; // POST /api/3ds'den dönen ID
const status = await getPaymentStatus(paymentId);
if (status.status === 'SUCCESS') {
// Ödeme başarılı
} else if (status.status === 'FAILED' || status.status === 'CANCELLED') {
// Ödeme başarısız
} else {
// Hâlâ işleniyor (ENROLLED), biraz bekle ve tekrar sorgula
}
Description: Cancel a previously completed payment. A new payment record with transaction type
CANCEL is created referencing the original payment.
Endpoint: POST /api/payment/{paymentId}/cancel
Request Body: None
Returns an ApiPaymentResponse for the newly created cancel transaction. Check
status field — SUCCESS means the cancellation was accepted.
SUCCESS status can be cancelled.parentPaymentId field referencing the original payment.
const cancelPayment = async (paymentId) => {
const response = await fetch(`https://api.example.com/api/payment/${paymentId}/cancel`, {
method: 'POST',
headers: {
'X-API-KEY': 'your_api_key_here',
'X-API-SECRET': 'your_api_secret_here'
}
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.message);
}
const result = await response.json();
if (result.status === 'SUCCESS') {
console.log('Payment cancelled successfully');
}
return result;
};
Description: Request a refund for a previously completed payment. Refund requests do not execute immediately — they are placed into an approval queue and processed after administrative approval.
Endpoint: POST /api/payment/{paymentId}/refund
{
"amount": "50.00",
"currency": "TRY"
}
Returns an ApiPaymentResponse with status: PENDING_APPROVAL. The response includes a
new paymentId for the refund transaction.
SUCCESS or CAPTURED status can be refunded.GET /api/payment/{refundPaymentId} to poll the refund status.PENDING_APPROVALSUCCESS or FAILEDREJECTED
const requestRefund = async (paymentId, amount, currency) => {
const response = await fetch(`https://api.example.com/api/payment/${paymentId}/refund`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-KEY': 'your_api_key_here',
'X-API-SECRET': 'your_api_secret_here'
},
body: JSON.stringify({ amount, currency })
});
const result = await response.json();
// result.status === 'PENDING_APPROVAL'
// result.paymentId → refund transaction ID (iade durumunu sorgulamak için)
return result;
};
// Refund durumu sorgulama
const checkRefundStatus = async (refundPaymentId) => {
const response = await fetch(`https://api.example.com/api/payment/${refundPaymentId}`, {
method: 'GET',
headers: {
'X-API-KEY': 'your_api_key_here',
'X-API-SECRET': 'your_api_secret_here'
}
});
const result = await response.json();
// result.status: PENDING_APPROVAL | SUCCESS | FAILED | REJECTED
return result;
};
Description: Receive real-time notifications when payment status changes occur. Webhooks are sent as HTTP POST requests to your configured endpoint.
When a payment transitions to a final state (SUCCESS, FAILED, REJECTED,
or CANCELLED), we send a webhook notification to your registered URL.
Every webhook request includes two security headers:
| Header | Description | Example |
|---|---|---|
x-request-time |
Unix timestamp in milliseconds when the webhook was generated | 1715150400000 |
x-request-signature |
HMAC SHA-256 hex digest of timestampMillis:payload |
5d41402abc4b2a76b9719d911017c592... |
x-event-id |
Unique identifier for the webhook event | 123e4567-e89b-12d3-a456-426614174000 |
x-event-type |
Event type (e.g., payment.status_changed) |
payment.status_changed |
Webhooks are sent as POST requests with a JSON body. The body is the ApiPaymentResponse object:
{
"paymentId": "UUID",
"orderId": "string",
"amount": "number",
"installmentCount": "number",
"currency": "string",
"merchantCommission": "number",
"status": "string",
"transactionType": "string",
"paymentDate": "date",
"cardHolderName": "string",
"pan": "string",
"domInt": "string",
"cardScheme": "string",
"cardType": "string",
"cardSubType": "string",
"loyaltyCode": "string",
"externalTransactionId": "string",
"authCode": "string",
"resultCode": "string",
"resultMessage": "string",
"customerId": "string"
}
The payload is an exact representation of the ApiPaymentResponse object. Please refer to the Payment Response field descriptions for more details.
You must verify the x-request-signature header to ensure the request is authentic
and has not been tampered with.
x-request-time header (epoch milliseconds).x-request-signature header (hex digest).x-request-time + ":" + raw_request_bodyx-request-signature header value. If they match, the
webhook is authentic.
const crypto = require('crypto');
function verifyWebhook(requestBody, timestampHeader, signatureHeader, secret) {
// 1. Construct signed content
const signedContent = timestampHeader + ':' + requestBody;
// 2. Compute expected signature
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(signedContent)
.digest('hex');
// 3. Compare signatures (timing-safe)
const isValid = crypto.timingSafeEqual(
Buffer.from(signatureHeader, 'hex'),
Buffer.from(expectedSignature, 'hex')
);
// 4. Check timestamp freshness (5 minutes = 300000ms)
const age = Date.now() - parseInt(timestampHeader);
if (age > 300000) {
return false; // Too old — possible replay attack
}
return isValid;
}
// Express.js webhook handler
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const timestamp = req.headers['x-request-time'];
const signature = req.headers['x-request-signature'];
const body = req.body.toString();
if (!verifyWebhook(body, timestamp, signature, 'your_webhook_secret')) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(body);
console.log('Payment status changed:', event.data.status);
// Process the webhook...
res.status(200).send('OK');
});
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.HexFormat;
public boolean verifySignature(String requestBody, String timestampHeader,
String signatureHeader, String secret) throws Exception {
// 1. Construct signed content
String signedContent = timestampHeader + ":" + requestBody;
// 2. Compute expected signature
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
String expectedSignature = HexFormat.of().formatHex(
mac.doFinal(signedContent.getBytes(StandardCharsets.UTF_8))
);
// 3. Compare signatures
if (!expectedSignature.equals(signatureHeader)) {
return false;
}
// 4. Check timestamp freshness (5 minutes = 300000ms)
long age = System.currentTimeMillis() - Long.parseLong(timestampHeader);
return age <= 300000;
}
import hmac
import hashlib
import time
def verify_webhook(request_body: str, timestamp_header: str,
signature_header: str, secret: str) -> bool:
# 1. Construct signed content
signed_content = f"{timestamp_header}:{request_body}"
# 2. Compute expected signature
expected_signature = hmac.new(
secret.encode(), signed_content.encode(), hashlib.sha256
).hexdigest()
# 3. Compare signatures (timing-safe)
if not hmac.compare_digest(signature_header, expected_signature):
return False
# 4. Check timestamp freshness (5 minutes = 300000ms)
age = int(time.time() * 1000) - int(timestamp_header)
return age <= 300000
The first delivery attempt is made immediately when the payment reaches a final state. If your endpoint does not
respond with an HTTP 2xx status code, we will retry the webhook with exponential backoff:
| Retry | Delay After Failure | Cumulative Time |
|---|---|---|
| 1 | 30 seconds | ~30s |
| 2 | 1 minute | ~1.5m |
| 3 | 5 minutes | ~6.5m |
| 4 | 15 minutes | ~21.5m |
| 5 | 1 hour | ~1.4h |
| 6 | 4 hours | ~5.4h |
| 7 | 12 hours | ~17.4h |
| 8+ | 24 hours | ~41.4h |
Retries will continue for up to 48 hours after the initial event. After this period, the webhook is permanently marked as failed.
2xx response within 5 seconds. Process the webhook
data asynchronously if needed.x-request-signature header using the
x-request-time header before processing the webhook.x-event-id header or paymentId field to deduplicate.GET /api/payment/{paymentId} or GET /api/checkout/{checkoutId}).