{"activeVersionTag":"latest","latestAvailableVersionTag":"latest","collection":{"info":{"_postman_id":"da0ba46b-336c-4dd3-88d2-b8adfa5fef73","name":"EkstraPAY V2 Integration Documentation","description":"## 📋 Overview\n\nEkstraPAY V2 is a payment gateway integration system that supports both deposit and withdraw operations. This documentation covers the complete integration process using Postman, including payment creation, transaction status checking, and callback handling.\n\n# Response Handling\n\nThe API returns a `status` field (`success` or `error`) and a `data.type` field that indicates the transaction state. Use both fields together to determine the correct action.\n\n---\n\n## Response Types\n\n| type | status | Description |\n| --- | --- | --- |\n| `new` | `success` | A new deposit transaction has been created. The response contains bank account details (IBAN, account holder, amount) that should be displayed to the customer to complete the bank transfer. |\n| `on_process` | `error` | The customer already has a pending transaction. A new transaction cannot be created until the existing one is completed or expires. The existing transaction details are returned. |\n| `update` | `error` | The customer had a pending transaction but sent a different amount. The existing transaction has been updated with the new amount instead of creating a new one. |\n| _(null)_ | `error` | A validation error occurred before any transaction processing (e.g. amount is out of the allowed range). The `data` field is not present. Check the `message` field for the error description. |\n\n---\n\n## Logic Example\n\n``` php\n$result = json_decode($response, true);\n$type = $result['data']['type'] ?? null;\nif ($result['status'] === 'success' && $type === 'new') {\n    // New transaction created successfully.\n    // Display bank details to the customer so they can complete the transfer.\n    $iban   = $result['data']['bank']['account_iban'];\n    $holder = $result['data']['bank']['account_holder'];\n    $amount = $result['data']['bank']['amount'];\n    $txId   = $result['data']['customer']['transaction_id'];\n} elseif ($result['status'] === 'error' && $type === 'on_process') {\n    // Customer already has an active transaction.\n    // Do not create a new request. Wait for the current one to complete or expire.\n    $txId = $result['data']['customer']['transaction_id'];\n} elseif ($result['status'] === 'error' && $type === 'update') {\n    // Amount changed — the existing transaction has been updated.\n    // No new transaction was created. The same transaction_id is preserved.\n    $txId   = $result['data']['customer']['transaction_id'];\n    $amount = $result['data']['bank']['amount'];\n} elseif ($result['status'] === 'error' && $type === null) {\n    // Validation error. No transaction was created or modified.\n    // Example: \"Amount must be between 100.00 ₺ and 100,000,000,000.00 ₺\"\n    $errorMessage = $result['message'];\n}\n\n ```\n\n---\n\n> **Note:** When `status` is `error` but `data` is present, it does not indicate a failure — it means the system returned an existing or updated transaction instead of creating a new one. Always check `data.type` to determine the actual outcome. \n  \n\n## **🔔 Please give us a callback response as follows**\n\n``` json\n{\n  \"status\": true,\n  \"message\": \"Callback Received\"\n}\n\n ```\n\n## 🔔 Hash Calculation\n\n``` php\n   $hash = implode(\":\", array_filter([\n            transaction_id,\n            customer_fullname,\n            customer_username,\n            customer_id,\n            customer_description,\n            floatval(request_amount),\n            floatval(request_final_amount)\n        ], function($value) {\n            return $value !== null && $value !== '';\n        }));\n// Export. hashing encode\nhash = hash = hash(\"sha256\",$hash) \n\n ```\n\n## **🔔 Hash Validation**\n\n``` php\n/**\n * Validates transaction hash\n * \n * @param array $transactionDetail Transaction detail array\n * @param string $hashToValidate Hash value to validate\n * @return bool Returns true if hash is valid, false otherwise\n */\nfunction validateTransactionHash(array $transactionDetail, string $hashToValidate): bool\n{\n    // Prepare values for hash generation\n    $hashComponents = array_filter([\n        $transactionDetail['transaction_id'] ?? null,\n        $transactionDetail['customer_fullname'] ?? null,\n        $transactionDetail['customer_username'] ?? null,\n        $transactionDetail['customer_id'] ?? null,\n        $transactionDetail['customer_description'] ?? null,\n        isset($transactionDetail['request_amount']) ? floatval($transactionDetail['request_amount']) : null,\n        isset($transactionDetail['request_final_amount']) ? floatval($transactionDetail['request_final_amount']) : null\n    ], function($value) {\n        return $value !== null && $value !== '';\n    });\n    // Join values with \":\"\n    $hashString = implode(\":\", $hashComponents);\n    // Generate SHA-256 hash\n    $calculatedHash = hash(\"sha256\", $hashString);\n    // Compare hashes (timing attack safe)\n    return hash_equals($calculatedHash, $hashToValidate);\n}\n// Usage example:\n$transactionData = [\n    'transaction_id' => 'TRX-2024-001',\n    'customer_fullname' => 'John Smith',\n    'customer_username' => 'jsmith',\n    'customer_id' => 'CUST-789456',\n    'customer_description' => 'Monthly subscription payment',\n    'request_amount' => 250.00,\n    'request_final_amount' => 262.50\n];\n// Hash to validate (received from API or stored in DB)\n$receivedHash = '7f3a2b9c8d4e5f6a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4';\n// Validate the hash\nif (validateTransactionHash($transactionData, $receivedHash)) {\n    echo \"Hash validation successful - Transaction is authentic!\";\n} else {\n    echo \"Hash validation failed - Transaction may be tampered!\";\n}\n\n ```\n\n## **🔔 Callback Configuration**\n\nAfter the payment process, you needed to provide us with Deposit and Withdrawal Callback URLs to notify you of the payment status. Additionally, please send us your Whitelist IP addresses for access.\n\n### **Requirements for Integration**\n\nFor a complete integration, you must provide EkstraPAY with your callback URLs to receive real-time transaction updates:\n\n1. **Deposit Callback URL** - Endpoint to receive deposit transaction status updates\n    \n2. **Withdraw Callback URL** - Endpoint to receive withdrawal transaction status updates\n    \n\n#### **Please provide us with the whitelist IP addresses for access.**\n\n## 🔄 Transaction Status Lifecycle\n\nAfter creating a payment request, transactions go through the following status stages:\n\n| Status Code | Status Name | Description |\n| --- | --- | --- |\n| PAY_NEW | New | Transaction has been created and is awaiting processing |\n| PAY_PROCESSING | Processing | Transaction is currently being processed |\n| PAY_REJECTED | Rejected | Transaction was rejected by the system or payment provider |\n| PAY_APPROVED | Approved | Transaction successfully completed and approved |\n| PAY_CANCELLED | Cancelled | Transaction was cancelled by user or system |\n| PAY_TIMEOUT | Timeout | Transaction expired due to timeout |\n| NOT_FOUND | Not Found | Transaction Not Found |\n\n**📝 Note:** Sample PHP code for this request is included in the request page notes. Please refer to the PHP examples provided in the Postman collection or check the Pre-request Script tab for implementation details.\n\n## 🔐 Authentication Headers\n\nAll requests require the following authentication headers:\n\nhttp\n\n``` bash\nX-API-Key: your_api_key\nX-Timestamp: unix_timestamp\nX-Nonce: random_32_char_hex\nX-Signature: hmac_sha256_signature\nContent-Type: application/json\n\n ```\n\n## 📨 Pre-request Script Configuration\n\nThe Postman collection includes Pre-request Scripts that automatically:\n\n- Generate timestamps and nonces\n    \n- Calculate HMAC-SHA256 signatures\n    \n- Set authentication headers\n    \n\n**Important:** Review the Pre-request Script tab in Postman for each request to understand the signature generation process. The scripts contain comments explaining each step.\n\n### 📚 Callback Examples in Postman\n\nThe Postman collection includes an **\"Example Callbacks\"** folder containing:\n\n- Sample deposit callback request\n    \n- Sample withdraw callback request\n    \n- Response validation examples\n    \n\n# **Request Structure**\n\nThe same endpoint handles both **deposit** and **withdraw** operations. The only difference between them is the JSON body — the URL, headers, and signature logic are identical for both.\n\n---\n\n**Endpoint**\n\n```\nPOST https://v2.ekstrapay.com/api/v1/payment/create\n\n ```\n\n---\n\n**How the signature is built**\n\nEvery request must be signed. The signing process is the same regardless of programming language:\n\n1. Get the current **Unix timestamp** in seconds (not milliseconds)\n    \n2. Generate a **random 32-character hex string** as nonce\n    \n3. Take your raw JSON body and **remove all fields that are empty string, null, or undefined**\n    \n4. **Serialize** the cleaned object to a compact JSON string (no extra whitespace)\n    \n5. **Concatenate** the following four values separated by pipe `|`:\n    \n    ```\n                      apiKey|timestamp|nonce|jsonString\n    \n     ```\n    \n6. Compute **HMAC-SHA256** of that string using your API secret as the key\n    \n7. The resulting hex digest is your signature\n    \n\n---\n\n**Required headers**\n\n| Header | Value |\n| --- | --- |\n| `X-API-Key` | Your API key |\n| `X-Timestamp` | Unix timestamp used in signature |\n| `X-Nonce` | Random hex nonce used in signature |\n| `X-Signature` | HMAC-SHA256 hex digest |\n| `Content-Type` | `application/json` |\n\n---\n\n**Request body — Deposit**\n\n| Field | Type | Required | Description |\n| --- | --- | --- | --- |\n| `operation` | string | ✅ | `\"deposit\"` |\n| `amount` | integer | ✅ | Payment amount |\n| `customer_fullname` | string | ✅ | Customer full name |\n| `customer_username` | string | ✅ | Customer username |\n| `customer_id` | string | ✅ | Customer unique ID |\n| `customer_email` | string | ✅ | Customer email |\n| `customer_site` | string | ✅ | Site name or domain |\n| `customer_phone` | string | ➖ | Optional |\n| `customer_gender` | string | ➖ | Optional |\n| `customer_hash` | string | ➖ | Optional |\n| `customer_description` | string | ➖ | Optional |\n| `order_id` | string | ➖ | Optional |\n\n**Request body — Withdraw**\n\n| Field | Type | Required | Description |\n| --- | --- | --- | --- |\n| `operation` | string | ✅ | `\"withdraw\"` |\n| `amount` | integer | ✅ | Payout amount |\n| `customer_fullname` | string | ✅ | Customer full name |\n| `customer_username` | string | ✅ | Customer username |\n| `customer_id` | string | ✅ | Customer unique ID |\n| `customer_site` | string | ✅ | Site name or domain |\n| `account_iban` | string | ✅ | Target IBAN |\n| `account_holder` | string | ✅ | IBAN holder name |\n| `customer_phone` | string | ➖ | Optional |\n| `customer_email` | string | ➖ | Optional |\n| `customer_hash` | string | ➖ | Optional |\n| `customer_description` | string | ➖ | Optional |\n| `order_id` | string | ➖ | Optional |\n\n---\n\n**Implementation notes — critical**\n\n**Empty field stripping** — Before signing, remove every field whose value is an empty string, null, or undefined. The server applies the same logic. If your JSON includes empty fields that the server stripped (or vice versa), the JSON strings won't match and the signature will fail.\n\n**JSON serialization** — Serialize with Unicode characters unescaped. Characters like `ş`, `ğ`, `ü`, `ı` must appear as-is in the JSON string, not as `\\uXXXX` escape sequences. Both sides must produce byte-for-byte identical JSON.\n\n**Timestamp unit** — Must be in **seconds**, not milliseconds. Using milliseconds will cause timestamp validation to fail on the server.\n\n**HMAC output format** — The signature must be a **lowercase hex string**. Most HMAC libraries output this by default, but verify this in your language of choice.\n\n**Pipe delimiter order is strict** — The payload must be exactly `apiKey|timestamp|nonce|jsonString`. Any reordering or extra characters will produce a mismatched signature.\n\n**Replay attack protection** — The combination of timestamp and nonce ensures each request is unique. Even if a valid signed request is intercepted, it cannot be reused because the timestamp will be considered stale and the nonce is single-use on the server side.\n\n---\n\n**Language-specific HMAC-SHA256 references**\n\n| Language | Method |\n| --- | --- |\n| PHP | `hash_hmac('sha256', $payload, $secret)` |\n| JavaScript | `CryptoJS.HmacSHA256(payload, secret).toString()` |\n| Python | `hmac.new(secret.encode(), payload.encode(), hashlib.sha256).hexdigest()` |\n| Java | `Mac.getInstance(\"HmacSHA256\")` |\n| C# | `HMACSHA256.ComputeHash(...)` |\n| Go | `hmac.New(sha256.New, []byte(secret))` |\n| Ruby | `OpenSSL::HMAC.hexdigest('sha256', secret, payload)` |","schema":"https://schema.getpostman.com/json/collection/v2.0.0/collection.json","isPublicCollection":false,"owner":"14352549","team":2451401,"collectionId":"da0ba46b-336c-4dd3-88d2-b8adfa5fef73","publishedId":"2sB3QMKUGL","public":true,"publicUrl":"https://documenter-api.postman.tech/view/14352549/2sB3QMKUGL","privateUrl":"https://go.postman.co/documentation/14352549-da0ba46b-336c-4dd3-88d2-b8adfa5fef73","customColor":{"top-bar":"FFFFFF","right-sidebar":"303030","highlight":"303030"},"documentationLayout":"classic-single-column","customisation":{"metaTags":[{"name":"description","value":""},{"name":"title","value":"EkstraPAY V2"}],"appearance":{"default":"system_default","themes":[{"name":"dark","logo":"https://content.pstmn.io/e6f1fa50-9cd4-461a-906f-26a37f3c543a/c2luZ2xlbG9nb2RhcmsucG5n","colors":{"top-bar":"212121","right-sidebar":"303030","highlight":"FFFFFF"}},{"name":"light","logo":"https://content.pstmn.io/c081941f-c596-45c6-a4c2-bb6153e1ee7e/c2luZ2xlbG9nb2RhcmtAM3gucG5n","colors":{"top-bar":"FFFFFF","right-sidebar":"303030","highlight":"303030"}}]}},"version":"8.12.3","publishDate":"2025-10-12T16:57:46.000Z","activeVersionTag":"latest","documentationTheme":"light","metaTags":{"title":"EkstraPAY V2","description":""},"logos":{"logoLight":"https://content.pstmn.io/c081941f-c596-45c6-a4c2-bb6153e1ee7e/c2luZ2xlbG9nb2RhcmtAM3gucG5n","logoDark":"https://content.pstmn.io/e6f1fa50-9cd4-461a-906f-26a37f3c543a/c2luZ2xlbG9nb2RhcmsucG5n"}},"statusCode":200},"environments":[],"user":{"authenticated":false,"permissions":{"publish":false}},"run":{"button":{"js":"https://run.pstmn.io/button.js","css":"https://run.pstmn.io/button.css"}},"web":"https://www.getpostman.com/","team":{"logo":"https://res.cloudinary.com/postman/image/upload/t_team_logo_pubdoc/v1/team/5719893e06cabb4bad79c18baca56cb2348c81464e4d17a5c3b06a66a46a407d","favicon":""},"isEnvFetchError":false,"languages":"[{\"key\":\"csharp\",\"label\":\"C#\",\"variant\":\"HttpClient\"},{\"key\":\"csharp\",\"label\":\"C#\",\"variant\":\"RestSharp\"},{\"key\":\"curl\",\"label\":\"cURL\",\"variant\":\"cURL\"},{\"key\":\"dart\",\"label\":\"Dart\",\"variant\":\"http\"},{\"key\":\"go\",\"label\":\"Go\",\"variant\":\"Native\"},{\"key\":\"http\",\"label\":\"HTTP\",\"variant\":\"HTTP\"},{\"key\":\"java\",\"label\":\"Java\",\"variant\":\"OkHttp\"},{\"key\":\"java\",\"label\":\"Java\",\"variant\":\"Unirest\"},{\"key\":\"javascript\",\"label\":\"JavaScript\",\"variant\":\"Fetch\"},{\"key\":\"javascript\",\"label\":\"JavaScript\",\"variant\":\"jQuery\"},{\"key\":\"javascript\",\"label\":\"JavaScript\",\"variant\":\"XHR\"},{\"key\":\"c\",\"label\":\"C\",\"variant\":\"libcurl\"},{\"key\":\"nodejs\",\"label\":\"NodeJs\",\"variant\":\"Axios\"},{\"key\":\"nodejs\",\"label\":\"NodeJs\",\"variant\":\"Native\"},{\"key\":\"nodejs\",\"label\":\"NodeJs\",\"variant\":\"Request\"},{\"key\":\"nodejs\",\"label\":\"NodeJs\",\"variant\":\"Unirest\"},{\"key\":\"objective-c\",\"label\":\"Objective-C\",\"variant\":\"NSURLSession\"},{\"key\":\"ocaml\",\"label\":\"OCaml\",\"variant\":\"Cohttp\"},{\"key\":\"php\",\"label\":\"PHP\",\"variant\":\"cURL\"},{\"key\":\"php\",\"label\":\"PHP\",\"variant\":\"Guzzle\"},{\"key\":\"php\",\"label\":\"PHP\",\"variant\":\"HTTP_Request2\"},{\"key\":\"php\",\"label\":\"PHP\",\"variant\":\"pecl_http\"},{\"key\":\"powershell\",\"label\":\"PowerShell\",\"variant\":\"RestMethod\"},{\"key\":\"python\",\"label\":\"Python\",\"variant\":\"http.client\"},{\"key\":\"python\",\"label\":\"Python\",\"variant\":\"Requests\"},{\"key\":\"r\",\"label\":\"R\",\"variant\":\"httr\"},{\"key\":\"r\",\"label\":\"R\",\"variant\":\"RCurl\"},{\"key\":\"ruby\",\"label\":\"Ruby\",\"variant\":\"Net::HTTP\"},{\"key\":\"shell\",\"label\":\"Shell\",\"variant\":\"Httpie\"},{\"key\":\"shell\",\"label\":\"Shell\",\"variant\":\"wget\"},{\"key\":\"swift\",\"label\":\"Swift\",\"variant\":\"URLSession\"}]","languageSettings":[{"key":"csharp","label":"C#","variant":"HttpClient"},{"key":"csharp","label":"C#","variant":"RestSharp"},{"key":"curl","label":"cURL","variant":"cURL"},{"key":"dart","label":"Dart","variant":"http"},{"key":"go","label":"Go","variant":"Native"},{"key":"http","label":"HTTP","variant":"HTTP"},{"key":"java","label":"Java","variant":"OkHttp"},{"key":"java","label":"Java","variant":"Unirest"},{"key":"javascript","label":"JavaScript","variant":"Fetch"},{"key":"javascript","label":"JavaScript","variant":"jQuery"},{"key":"javascript","label":"JavaScript","variant":"XHR"},{"key":"c","label":"C","variant":"libcurl"},{"key":"nodejs","label":"NodeJs","variant":"Axios"},{"key":"nodejs","label":"NodeJs","variant":"Native"},{"key":"nodejs","label":"NodeJs","variant":"Request"},{"key":"nodejs","label":"NodeJs","variant":"Unirest"},{"key":"objective-c","label":"Objective-C","variant":"NSURLSession"},{"key":"ocaml","label":"OCaml","variant":"Cohttp"},{"key":"php","label":"PHP","variant":"cURL"},{"key":"php","label":"PHP","variant":"Guzzle"},{"key":"php","label":"PHP","variant":"HTTP_Request2"},{"key":"php","label":"PHP","variant":"pecl_http"},{"key":"powershell","label":"PowerShell","variant":"RestMethod"},{"key":"python","label":"Python","variant":"http.client"},{"key":"python","label":"Python","variant":"Requests"},{"key":"r","label":"R","variant":"httr"},{"key":"r","label":"R","variant":"RCurl"},{"key":"ruby","label":"Ruby","variant":"Net::HTTP"},{"key":"shell","label":"Shell","variant":"Httpie"},{"key":"shell","label":"Shell","variant":"wget"},{"key":"swift","label":"Swift","variant":"URLSession"}],"languageOptions":[{"label":"C# - HttpClient","value":"csharp - HttpClient - C#"},{"label":"C# - RestSharp","value":"csharp - RestSharp - C#"},{"label":"cURL - cURL","value":"curl - cURL - cURL"},{"label":"Dart - http","value":"dart - http - Dart"},{"label":"Go - Native","value":"go - Native - Go"},{"label":"HTTP - HTTP","value":"http - HTTP - HTTP"},{"label":"Java - OkHttp","value":"java - OkHttp - Java"},{"label":"Java - Unirest","value":"java - Unirest - Java"},{"label":"JavaScript - Fetch","value":"javascript - Fetch - JavaScript"},{"label":"JavaScript - jQuery","value":"javascript - jQuery - JavaScript"},{"label":"JavaScript - XHR","value":"javascript - XHR - JavaScript"},{"label":"C - libcurl","value":"c - libcurl - C"},{"label":"NodeJs - Axios","value":"nodejs - Axios - NodeJs"},{"label":"NodeJs - Native","value":"nodejs - Native - NodeJs"},{"label":"NodeJs - Request","value":"nodejs - Request - NodeJs"},{"label":"NodeJs - Unirest","value":"nodejs - Unirest - NodeJs"},{"label":"Objective-C - NSURLSession","value":"objective-c - NSURLSession - Objective-C"},{"label":"OCaml - Cohttp","value":"ocaml - Cohttp - OCaml"},{"label":"PHP - cURL","value":"php - cURL - PHP"},{"label":"PHP - Guzzle","value":"php - Guzzle - PHP"},{"label":"PHP - HTTP_Request2","value":"php - HTTP_Request2 - PHP"},{"label":"PHP - pecl_http","value":"php - pecl_http - PHP"},{"label":"PowerShell - RestMethod","value":"powershell - RestMethod - PowerShell"},{"label":"Python - http.client","value":"python - http.client - Python"},{"label":"Python - Requests","value":"python - Requests - Python"},{"label":"R - httr","value":"r - httr - R"},{"label":"R - RCurl","value":"r - RCurl - R"},{"label":"Ruby - Net::HTTP","value":"ruby - Net::HTTP - Ruby"},{"label":"Shell - Httpie","value":"shell - Httpie - Shell"},{"label":"Shell - wget","value":"shell - wget - Shell"},{"label":"Swift - URLSession","value":"swift - URLSession - Swift"}],"layoutOptions":[{"value":"classic-single-column","label":"Single Column"},{"value":"classic-double-column","label":"Double Column"}],"versionOptions":[],"environmentOptions":[{"value":"0","label":"No Environment"}],"canonicalUrl":"https://documenter.gw.postman.com/view/metadata/2sB3QMKUGL"}