### Finik API Error Response Examples Source: https://telegra.ph/Finikkg-09-10/index A collection of sample error responses from the Finik API. These examples illustrate common issues such as invalid timestamps, amounts, signatures, and authorization problems, providing status codes and error messages. ```json { "statusCode": 400, "ErrorMessage": "A valid UNIX timestamp must be provided in the header" } ``` ```json { "statusCode": 400, "ErrorMessage": "An invalid amount is provided. Amount must be greater than 0." } ``` ```json { "statusCode": 401, "ErrorMessage": "An invalid signature is provided" } ``` ```json { "statusCode": 403, "ErrorMessage": "Forbidden" } ``` -------------------------------- ### Finik Webhook Request Example Source: https://telegra.ph/Finikkg-09-10/index An example of an inbound POST request sent by Finik to a merchant's webhook URL. It includes typical headers like `Content-Type`, `x-api-timestamp`, and `signature`, along with a JSON payload containing transaction details and status. ```http POST /webhooks/finik HTTP/1.1Host: merchant.example.com Content-Type: application/json x-api-timestamp: 1737369012345 signature: Gk3GqQ1O0z+4wC3…(Base64)…u1U= { "id": "transaction-id-15423_CREDIT", "transactionId": "transaction-id-241234", "status": "SUCCEEDED", // or "FAILED" "amount": 100, "net": 100, "accountId": "your account id", "fields": { "amount": 100, "fieldId1": "value1", "fieldId2": "value2" }, "requestDate": 1737369012345, "transactionDate": 1737369012345, "transactionType": "DEBIT", "receiptNumber": "some-number" } ``` -------------------------------- ### Format Headers for Signature Generation Source: https://telegra.ph/Finikkg-09-10/index This pseudocode demonstrates how to format and concatenate headers, including the 'Host' header and headers starting with 'x-api-*', after sorting them alphabetically by name. ```pseudocode parts = "host" + ":" + String(header["Host"]) + "&" parts += Lowercase() + ":" + String() + "&" parts += Lowercase() + ":" + String() + "&" ... parts += Lowercase() + ":" + String() ``` -------------------------------- ### Node.js Payment Integration Source: https://telegra.ph/Finikkg-09-10/index Integrate Finik payments in Node.js using the @mancho.devs/authorizer package. This example demonstrates how to construct and sign a payment request. ```APIDOC ## POST /v1/payment ### Description Initiates a payment request to the Finik API. This endpoint is used for processing payments, typically with QR code or other payment methods. ### Method POST ### Endpoint /v1/payment ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **Amount** (number) - Required - The amount to be paid. - **CardType** (string) - Required - The type of card or payment method (e.g., 'FINIK_QR'). - **PaymentId** (string) - Required - A unique identifier for the payment (UUID format). - **RedirectUrl** (string) - Required - The URL to redirect the user after payment. - **Data** (object) - Required - Additional data for the payment. - **accountId** (string) - Required - The account ID associated with the merchant. - **merchantCategoryCode** (string) - Required - The merchant's category code. - **name_en** (string) - Required - The name of the merchant in English. ### Request Example ```json { "Amount": 100, "CardType": "FINIK_QR", "PaymentId": "00000000-0000-0000-0000-000000000000", "RedirectUrl": "https://example.com/success", "Data": { "accountId": "your-account-id", "merchantCategoryCode": "0742", "name_en": "your-qr-name" } } ``` ### Response #### Success Response (201 or 302) - **paymentId** (string) - The ID of the created payment. - **paymentUrl** (string) - The URL to complete the payment (if redirected). - **status** (string) - The status of the payment. #### Response Example (201) ```json { "paymentId": "some-uuid", "paymentUrl": "https://payment.finik.kg/...". "status": "PENDING" } ``` #### Response Example (302 Redirect) Location header contains the redirect URL. ``` -------------------------------- ### Node.js: Prevent Redirects with Axios Source: https://telegra.ph/Finikkg-09-10/index Illustrates how to configure the Axios library in Node.js to avoid following redirects automatically. This is achieved by setting `maxRedirects: 0` and handling the 'Location' header from a 302 response to get the payment URL. ```javascript const res = await axios.post(url, body, { headers: { "x-api-key": apiKey, "x-api-timestamp": ts, signature }, maxRedirects: 0, // <-- don't auto-follow validateStatus: s => [201, 302].includes(s) || s >= 400 });const paymentUrl = res.headers.location; ``` -------------------------------- ### Create Payment Source: https://telegra.ph/Finikkg-09-10/index Initiates a payment process by sending payment details to the Finik API. The API responds with a 302 redirect containing a payment URL in the `Location` header. It is crucial not to auto-follow this redirect but to extract the URL and present it to the user. ```APIDOC ## POST /v1/payment ### Description Initiates a payment process. The API responds with a 302 redirect containing a payment URL in the `Location` header. Do not auto-follow the redirect; instead, extract the URL and present it to the user for payment completion. ### Method POST ### Endpoint /v1/payment ### Parameters #### Headers - **x-api-key** (string) - Required - Your API key. - **x-api-timestamp** (string) - Required - A valid UNIX timestamp. - **signature** (string) - Required - The signature generated for the request. - **content-type** (string) - Required - `application/json`. #### Request Body - **Amount** (number) - Required - The payment amount. - **CardType** (string) - Required - The card type, e.g., `FINIK_QR`. - **RedirectUrl** (string) - Required - The URL to redirect the user to after payment. ### Request Example ```json { "Amount": 100, "CardType": "FINIK_QR", "RedirectUrl": "https://your-merchant-site.com/payment-complete" } ``` ### Response #### Success Response (302 Found) - **Location** (string) - The URL of the payment page. #### Response Example ``` HTTP/1.1 302 Found Location: https://qr.finik/ ``` ### Error Handling - **400 Bad Request**: Invalid input, e.g., invalid timestamp or amount. ```json { "statusCode": 400, "ErrorMessage": "A valid UNIX timestamp must be provided in the header" } ``` ```json { "statusCode": 400, "ErrorMessage": "An invalid amount is provided. Amount must be greater than 0." } ``` - **401 Unauthorized**: Invalid signature. ```json { "statusCode": 401, "ErrorMessage": "An invalid signature is provided" } ``` - **403 Forbidden**: Access denied. ```json { "statusCode": 403, "ErrorMessage": "Forbidden" } ``` ``` -------------------------------- ### Format Query Parameters for Signature Generation Source: https://telegra.ph/Finikkg-09-10/index This pseudocode illustrates the process of collecting and concatenating query parameters for signature generation. It involves sorting parameters alphabetically by name and URI encoding both names and values. ```pseudocode parts = URiEncode() + "=" + URiEncode() + "&" parts += URiEncode() + "=" + URiEncode() + "&" ... parts += URiEncode() + "=" + URiEncode() // If the query parameter value is empty, use an empty string as a value. parts += URiEncode("acl") + "=" + "" ``` -------------------------------- ### Sign Finik API Requests with Python Source: https://telegra.ph/Finikkg-09-10/index Demonstrates how to use the python-authorizer package in Python to create an authenticated POST request to the Finik API. It covers setting up credentials, constructing the request payload, generating the signature, and sending the request using the requests library. ```python from authorizer import Signer import json import os import time import requests from urllib.parse import urljoin # Choose environment BASE_URL = "https://api.acquiring.averspay.kg" # prod # BASE_URL = "https://beta.api.acquiring.averspay.kg" # beta HOST = "api.acquiring.averspay.kg" # must match BASE_URL host exactly # HOST = "beta.api.acquiring.averspay.kg" API_KEY = os.environ["FINIK_API_KEY"] # from Finik PRIVATE_KEY_PEM = os.environ["FINIK_PRIVATE_PEM"] # contents of finik_private.pem timestamp = str(int(time.time() * 1000)) # UNIX ms body = { "Amount": 100, "CardType": "FINIK_QR", "PaymentId": "00000000-0000-0000-0000-000000000000", # use a real UUID "RedirectUrl": "https://example.com/success", "Data": { "accountId": "your-account-id", "merchantCategoryCode": "0742", "name_en": "your-qr-name" } } request_data = { "http_method": "POST", "path": "/v1/payment", "headers": { "Host": HOST, # must equal BASE_URL's host "x-api-key": API_KEY, "x-api-timestamp": timestamp, }, # If you have queries, set a dict here; signer will encode/sort them "query_string_parameters": None, "body": body, # plain dict; signer will canonicalize/JSON-stringify } # Produce Base64 RSA-SHA256 signature signature = Signer(**request_data).sign(PRIVATE_KEY_PEM) # Send the request url = urljoin(BASE_URL, request_data["path"]) resp = requests.post( url, headers={ "content-type": "application/json", "x-api-key": API_KEY, "x-api-timestamp": timestamp, "signature": signature, }, data=json.dumps(body, separators=( "," , ":" )), # prevent auto-follow to get payment link in the header.location: allow_redirects=False, ) if resp.status_code == 201: print("Created:", resp.json()) # -> { paymentId, paymentUrl, status } elif resp.status_code in (301, 302, 303, 307, 308): print("Redirect to:", resp.headers.get("Location")) else: print(resp.status_code, resp.text) ``` -------------------------------- ### Generate SSL Keys using OpenSSL Source: https://telegra.ph/Finikkg-09-10/index Generates private and public key certificates using OpenSSL commands. The private key is used to sign requests, and the public key is shared with Finik representatives for verification. Keep the private key secret. ```bash openssl genrsa -out finik_private.pem 2048 openssl rsa -in finik_private.pem -pubout > finik_public.pem ``` -------------------------------- ### Create Payment API Source: https://telegra.ph/Finikkg-09-10/index Endpoint for creating a new payment. Requires a signature generated from request details. ```APIDOC ## POST /v1/payment ### Description Creates a new payment transaction. A valid signature, API key, and timestamp are required in the headers. The request body contains payment details and associated data. ### Method POST ### Endpoint - Beta: `https://beta.api.acquiring.averspay.kg/v1/payment` - Production: `https://api.acquiring.averspay.kg/v1/payment` ### Headers - **signature** (String, Required): Authorization signature generated from request components. - **x-api-key** (String, Required): API client key provided by Finik. - **x-api-timestamp** (String, Required): Current timestamp in milliseconds (UNIX format). ### Request Body Parameters - **Amount** (Number, Required): The fixed amount for the QR code generation. - **CardType** (String, Required): Must be `FINIK_QR`. - **PaymentId** (String, Required): A unique identifier for the payment request. - **RedirectUrl** (String, Required): The URL to redirect to upon successful payment. - **Data** (JSON Object, Required): Contains additional required variables: - **accountId** (String, Required): Finik account ID for fund deposit. - **merchantCategoryCode** (String, Required): Merchant Category Code (MCC). - **name_en** (String, Required): Name displayed to the client during payment. - **webhookUrl** (String, Required): URL for receiving payment status notifications. ### Request Example ```json { "Amount": 100, "CardType": "FINIK_QR", "PaymentId": "your-unique-payment-id", "RedirectUrl": "https://example.com/success", "Data": { "accountId": "your-account-id", "merchantCategoryCode": "0742", "name_en": "your-qr-name", "webhookUrl": "https://your-webhook.com/status" } } ``` ### Response #### Success Response (200 OK) (Details of success response are not provided in the input text.) #### Error Response (Details of error responses are not provided in the input text.) ``` -------------------------------- ### Sign Finik API Requests with Node.js Source: https://telegra.ph/Finikkg-09-10/index Demonstrates how to use the @mancho.devs/authorizer package in Node.js to create an authenticated POST request to the Finik API. It covers setting up credentials, constructing the request payload, generating the signature, and sending the request using node-fetch. ```javascript import { Signer, RequestData } from '@mancho.devs/authorizer'; import fetch from 'node-fetch'; // or axios // Choose the environment you call: const baseUrl = 'https://api.acquiring.averspay.kg'; // prod // const baseUrl = 'https://beta.api.acquiring.averspay.kg'; // beta // IMPORTANT: Host header must match the URL host exactly. const host = new URL(baseUrl).host; // Your credentials const apiKey = process.env.FINIK_API_KEY!; // from Finik const privateKey = process.env.FINIK_PRIVATE_PEM!; // contents of finik_private.pem const timestamp = Date.now().toString(); // UNIX ms // Create Payment body per spec const body = { Amount: 100, CardType: 'FINIK_QR', PaymentId: '00000000-0000-0000-0000-000000000000', // use a real UUID RedirectUrl: 'https://example.com/success', Data: { accountId: 'your-account-id', merchantCategoryCode: '0742', name_en: 'your-qr-name', }, }; // Build the canonical input for signing const requestData: RequestData = { httpMethod: 'POST', path: '/v1/payment', // absolute path only (no query) headers: { Host: host, // must match baseUrl host 'x-api-key': apiKey, // included in signature 'x-api-timestamp': timestamp, // UNIX ms; same value used in signature // You may send other headers, but only `host` and `x-api-*` are included in the signature. }, queryStringParameters: undefined, // or { ... } if you have query; lib sorts & encodes body, }; // Produce Base64 RSA-SHA256 signature const signature = await new Signer(requestData).sign(privateKey); // Send the actual HTTP request const url = `${baseUrl}${requestData.path}`; const res = await fetch(url, { method: requestData.httpMethod, headers: { 'content-type': 'application/json', 'x-api-key': apiKey, 'x-api-timestamp': timestamp, signature, // <- attach signature header }, body: JSON.stringify(body), // If your API currently returns 302 (HTML) on S2S calls, prevent auto-follow: redirect: 'manual', // remove once API returns 201 JSON by default }); if (res.status === 302) { // Read Location when you opt into redirects console.log('Redirect to:', res.headers.get('location')); } else { console.error(res.status, await res.text()); } ``` -------------------------------- ### Generate Signature Data (Pseudocode) Source: https://telegra.ph/Finikkg-09-10/index This pseudocode outlines the steps to collect data for generating a signature. It involves concatenating the HTTP method, absolute URI path, specific headers, query parameters, and the JSON request body in a defined order. ```pseudocode 1) data = Lowercase(HTTP method) + "\n" 2) data += URIAbsolutePath + "\n" 3) data += ( header["Host"] and headers that start with x-api-*) + "\n" 4) data += queryStringParams + "\n" // Note: Don't add "\n" to data if there are no queryStringParams 5) data += json(request.body) ``` -------------------------------- ### Signature Generation Pseudocode Source: https://telegra.ph/Finikkg-09-10/index Pseudocode illustrating the steps to collect data and generate a signature for API requests. ```APIDOC ## Signature Generation ### Description This section details the pseudocode for generating a signature required for API requests. The signature is built by concatenating various components of the request in a specific order. ### Method Pseudocode ### Endpoint N/A ### Parameters #### Request Data Components - **HTTP Method**: The HTTP method of the request (e.g., POST, GET). - **Absolute URI Path**: The absolute path of the request URI. - **Host Header**: The value of the 'Host' header. - **X-API-* Headers**: All headers starting with 'x-api-*'. - **Query String Parameters**: All parameters in the query string. - **Request Body**: The JSON payload of the request. ### Pseudocode Steps 1. `data = Lowercase(HTTP method)` 2. `data += URIAbsolutePath` 3. `data += (header["Host"] and headers that start with x-api-*)` 4. `data += queryStringParams` (Append newline only if query string parameters exist) 5. `data += json(request.body)` ### Header Processing Rules 1. Convert all header names to lowercase. 2. Append the URI path starting with '/' and ending before any query parameters. 3. Concatenate headers as follows: - Include the 'Host' header. - Include all headers starting with 'x-api-*'. - Sort all included headers alphabetically by name. - Format: `lowercase(header_name):header_value&` ### Query Parameter Processing Rules 1. Sort query parameter names alphabetically. 2. Concatenate in the format: `URiEncode(parameter_name)=URiEncode(parameter_value)&` 3. If a query parameter value is empty, use an empty string (e.g., `parameter_name=`). ### Request Body Processing Rules 1. Sort JSON object keys alphabetically. 2. Stringify the JSON object with sorted keys. ``` -------------------------------- ### Sign Payload with RSA Private Key (Java) Source: https://telegra.ph/Finikkg-09-10/index This Java code snippet demonstrates how to sign a given payload using an RSA private key. It reads the private key from a file, initializes a SHA256withRSA signature instance, updates it with the payload, and returns the base64 encoded signature. ```java public String sign(String payload, String privatePath) { try{ Signature signature = Signature.getInstance("SHA256withRSA"); String privateKeyFile = new String(Files.readAllBytes(Paths.get(privatePath))); RSAKey rsaKey = (RSAKey) JWK.parseFromPEMEncodedObjects(privateKeyFile); PrivateKey privateKey = rsaKey.toPrivateKey(); signature.initSign(privateKey); signature.update(payload.getBytes(StandardCharsets.UTF_8)); byte[] signatureValue = signature.sign(); return Base64.encodeBase64String(signatureValue); }catch (Exception e){ throw new AppException(e.getMessage()); } } ``` -------------------------------- ### Telegraph Beta Public Key Source: https://telegra.ph/Finikkg-09-10/index This is the public key to be used when working with the beta environment of Telegraph. It allows developers to test integrations and features in a pre-production setting. ```text -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwlrlKz/8gLWd1ARWGA/8 o3a3Qy8G+hPifyqiPosiTY6nCHovANMIJXk6DH4qAqqZeLu8pLGxudkPbv8dSyG7 F9PZEAryMPzjoB/9P/F6g0W46K/FHDtwTM3YIVvstbEbL19m8yddv/xCT9JPPJTb LsSTVZq5zCqvKzpupwlGS3Q3oPyLAYe+ZUn4Bx2J1WQrBu3b08fNaR3E8pAkCK27 JqFnP0eFfa817VCtyVKcFHb5ij/D0eUP519Qr/pgn+gsoG63W4pPHN/pKwQUUiAy uLSHqL5S2yu1dffyMcMVi9E/Q2HCTcez5OvOllgOtkNYHSv9pnrMRuws3u87+hNT ZwIDAQAB -----END PUBLIC KEY----- ``` -------------------------------- ### Create Payment Request Body Source: https://telegra.ph/Finikkg-09-10/index This JSON object represents the request body for creating a payment. It includes fields like Amount, CardType, PaymentId, RedirectUrl, and a Data object containing account-specific details. ```json { "Amount": 100, "CardType": "FINIK_QR", "PaymentId": uuid(), "RedirectUrl": "https://example.com/success", "Data": { "accountId": "your-account-id", "merchantCategoryCode": "0742", "name_en": "your-qr-name", "webhookUrl": "your-payment-status-webhook-api" } } ``` -------------------------------- ### Payments Status Webhook (Callback) Source: https://telegra.ph/Finikkg-09-10/index Finik sends a POST request to your specified webhook URL after each completed payment. You must verify the request's authenticity using the same signature algorithm as the Create Payment endpoint and then process the payment status. ```APIDOC ## POST /webhooks/finik ### Description Receives payment status updates from Finik via a POST request to your webhook URL. Verify the request signature and timestamp, then update your order status based on the provided `status` field. ### Method POST ### Endpoint /webhooks/finik ### Parameters #### Headers - **Host** (string) - The host of your webhook server. - **Content-Type** (string) - `application/json`. - **x-api-timestamp** (string) - Required - The timestamp of the incoming request. - **signature** (string) - Required - The signature of the incoming request, generated using Finik's public key and the request details. #### Request Body - **id** (string) - Unique identifier for the transaction. - **transactionId** (string) - Finik's transaction ID. - **status** (string) - The status of the payment (e.g., "SUCCEEDED", "FAILED"). - **amount** (number) - The amount of the transaction. - **net** (number) - The net amount after fees. - **accountId** (string) - Your account ID with Finik. - **fields** (object) - Additional fields related to the transaction. - **requestDate** (number) - Timestamp when the payment was requested. - **transactionDate** (number) - Timestamp when the transaction was processed. - **transactionType** (string) - Type of transaction (e.g., "DEBIT"). - **receiptNumber** (string) - Receipt number for the transaction. ### Inbound Request Example ```json { "id": "transaction-id-15423_CREDIT", "transactionId": "transaction-id-241234", "status": "SUCCEEDED", "amount": 100, "net": 100, "accountId": "your account id", "fields": { "amount": 100, "fieldId1": "value1", "fieldId2": "value2" }, "requestDate": 1737369012345, "transactionDate": 1737369012345, "transactionType": "DEBIT", "receiptNumber": "some-number" } ``` ### How to Handle 1. **Build Canonical String**: Use the incoming request's method, path, headers (including `host` and all `x-api-*` headers), query parameters, and body to construct a canonical string. 2. **Verify Signature**: Compare the `signature` header with the signature generated from the canonical string using Finik's public key. 3. **Check Timestamp**: Ensure the `x-api-timestamp` header is within an acceptable time skew (e.g., ±5 minutes). 4. **Process Idempotently**: Use `transactionId` or `id` to prevent duplicate processing and update order status based on the `status` field. 5. **Acknowledge Quickly**: Respond with `200 OK` to acknowledge receipt promptly. Perform heavy processing asynchronously. ### Response #### Success Response (200 OK) A `200 OK` response acknowledges receipt of the webhook. ### Error Handling - If the signature or timestamp is invalid, Finik may retry the webhook or log an error. Ensure your webhook is reliable and acknowledges requests correctly. ``` -------------------------------- ### Node.js: Prevent Redirects with Fetch API Source: https://telegra.ph/Finikkg-09-10/index Demonstrates how to prevent the Fetch API in Node.js from automatically following HTTP redirects. It shows how to capture the 'Location' header from a 302 response, which is necessary for obtaining the payment URL. ```javascript const res = await fetch("https://api.acquiring.averspay.kg/v1/payment", { method: "POST", headers: { "content-type": "application/json", "x-api-key": apiKey, "x-api-timestamp": ts, signature }, body: JSON.stringify(body), redirect: "manual" // <-- don't auto-follow });if (res.status === 302) { const paymentUrl = res.headers.get("location"); // send this to the browser } ``` -------------------------------- ### cURL: Handling 302 Redirects Manually Source: https://telegra.ph/Finikkg-09-10/index Provides a cURL command for making a POST request to the Finik API. It explains that cURL by default does not auto-follow redirects, and the user should manually inspect the 'Location' header in the 302 response to find the payment URL. ```bash curl -i -X POST https://api.acquiring.averspay.kg/v1/payment \ -H 'x-api-key: ...' -H 'x-api-timestamp: ...' -H 'signature: ...' \ -H 'content-type: application/json' \ --data '{"Amount":100,"CardType":"FINIK_QR", ... }' # Look at the Location header in the 302 response ``` -------------------------------- ### Python: Prevent Redirects with Requests Source: https://telegra.ph/Finikkg-09-10/index Shows how to use the `requests` library in Python to prevent automatic redirection following. By setting `allow_redirects=False`, the code can inspect the 302 response headers to extract the payment URL from the 'Location' field. ```python resp = requests.post(url, headers=hdrs, json=body, allow_redirects=False) # <-- don't auto-follow if resp.status_code == 302: payment_url = resp.headers["Location"] ``` -------------------------------- ### Telegraph Production Public Key Source: https://telegra.ph/Finikkg-09-10/index This is the public key to be used when working with the production environment of Telegraph. It's essential for secure communication and verification with the production API. ```text -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuF/PUmhMPPidcMxhZBPb BSGJoSphmCI+h6ru8fG8guAlcPMVlhs+ThTjw2LHABvciwtpj51ebJ4EqhlySPyT hqSfXI6Jp5dPGJNDguxfocohaz98wvT+WAF86DEglZ8dEsfoumojFUy5sTOBdHEu g94B4BbrJvjmBa1YIx9Azse4HFlWhzZoYPgyQpArhokeHOHIN2QFzJqeriANO+wV aUMta2AhRVZHbfyJ36XPhGO6A5FYQWgjzkI65cxZs5LaNFmRx6pjnhjIeVKKgF99 4OoYCzhuR9QmWkPl7tL4Kd68qa/xHLz0Psnuhm0CStWOYUu3J7ZpzRK8GoEXRcr8 tQIDAQAB -----END PUBLIC KEY----- ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.