Create a payout
Endpoint
Headers
| Header | Value |
|---|---|
| Content-Type | application/json |
| Account | Your account uuid, you can find in Accounts |
| Sign | Request signature |
Certificate Setup
To generate API certificate for payouts, visit: https://merchant.alikassa.io/cabinet/form/setting-api-certs
Save the archive and unpack to get:
password.txtprivate.pempublic.pem
We keep only public.pem for signature verification.
Signature Generation
Wrap all POST data in JSON (in the same order) and sign:
$data = json_encode($data);
$privateKey = openssl_pkey_get_private(
file_get_contents('private.pem'),
file_get_contents('password.txt')
);
if ($privateKey === false) {
throw new \Exception('Error cert.');
}
openssl_sign($data, $sign, $privateKey);
$sign = base64_encode($sign);
import { readFileSync } from 'fs';
import { createSign } from 'crypto';
const dataStr = JSON.stringify(data);
const privateKeyPem = readFileSync('private.pem', 'utf8');
const passphrase = readFileSync('password.txt', 'utf8').trim();
const signer = createSign('SHA1');
signer.update(dataStr);
signer.end();
const sign = signer.sign({ key: privateKeyPem, passphrase }, 'base64');
import json
import base64
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding
data_bytes = json.dumps(data).encode('utf-8')
with open('password.txt', 'rb') as f:
password = f.read().strip()
with open('private.pem', 'rb') as f:
private_key = serialization.load_pem_private_key(f.read(), password=password)
signature = private_key.sign(
data_bytes,
padding.PKCS1v15(),
hashes.SHA1()
)
sign = base64.b64encode(signature).decode('utf-8')
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.Signature;
import java.util.Base64;
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
byte[] dataBytes = new com.fasterxml.jackson.databind.ObjectMapper().writeValueAsBytes(data);
char[] pass = Files.readString(Paths.get("password.txt")).toCharArray();
PEMParser parser = new PEMParser(Files.newBufferedReader(Paths.get("private.pem")));
PEMEncryptedKeyPair ckp = (PEMEncryptedKeyPair) parser.readObject();
var privateKey = new JcaPEMKeyConverter()
.getKeyPair(
ckp.decryptKeyPair(new JcePEMDecryptorProviderBuilder().build(pass))
)
.getPrivate();
Signature sig = Signature.getInstance("SHA1withRSA");
sig.initSign(privateKey);
sig.update(dataBytes);
String sign = Base64.getEncoder().encodeToString(sig.sign());
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
"io/ioutil"
)
dataBytes, _ := json.Marshal(data)
pemBytes, _ := ioutil.ReadFile("private.pem")
passBytes, _ := ioutil.ReadFile("password.txt")
block, _ := pem.Decode(pemBytes)
der, _ := x509.DecryptPEMBlock(block, passBytes)
priv, _ := x509.ParsePKCS1PrivateKey(der)
hash := sha1.Sum(dataBytes)
sigBytes, _ := rsa.SignPKCS1v15(rand.Reader, priv, crypto.SHA1, hash[:])
sign := base64.StdEncoding.EncodeToString(sigBytes)
Pass the received $sign in the Sign header.
Request Parameters
* - Required fields
** - When creating a payout to an account in EUR currency - required
| Name | Type | Description | Example |
|---|---|---|---|
| amount* | string | Amount | "1000.44" |
| number* | string | Account number, card | |
| order_id* | string (128) | Your id must be unique | |
| service* | string (100) | Service (Account, Acceptance Methods) | payment_card_rub |
| notification_endpoint_id | int | Notification id | |
| notification_endpoint_url | string|max:255 | Link to send a callback after the statuses are finalized | |
| extra** | array | Accepts optional parameters to increase conversion | |
| extra["card_exp_year"]** | string (2) | Bank card expiration date (year) | "24" |
| extra["card_exp_month"]** | string (2) | Bank card expiration date (month) | "01" |
| extra["card_holder"]** | string (100) | Cardholder's Name | "CARDHOLDER NAME" |
| extra["card_country"]** | string (2) Alpha-2 ISO 3166-1 | Bank card country | "UA" |
| extra["card_recipient_birth_date"]** | string | Cardholder's birthday | "1999-12-15" |
| customer_phone | string | Customer phone | "79001112233" |
| customer_email | string|email | Customer email | "[email protected]" |
| customer_code | string | Customer code | "sberbank" |
| customer_first_name | string | Customer first name | "IVAN" |
| customer_last_name | string | Customer last name | "IVANOV" |
| customer_country | string | Customer country | "DEU" |
| project_url | string | Url to the project (for aggregators) | "https://example.com" |
Extra Parameters Example
[
"card_exp_year" => "24",
"card_exp_month" => "01",
"card_holder" => "CARDHOLDER NAME",
"card_country" => "UA",
"card_recipient_birth_date" => "1999-12-15",
]
Response
| Name | Description |
|---|---|
| id | Id AliKassa |
| payment_status | Payment status wait — in the process of payment |
When creating, always wait, check the status of the payment via the API or wait for a notification!!
Response Examples
200 OK — Successful response.
The request was processed correctly. The payout has been created and is awaiting processing.
{
"payment_status": "wait",
"id": 100001524
}
HTTP CODE 400 — Error while creating the payout.
It may be caused by validation failures, limit restrictions, anti-fraud checks, or other constraints.
Such payouts are not created on our side and should be considered unsuccessful.
{
"message": "The given data was invalid.",
"errors": {
// ...
}
}
*Technical maintenance is in progress, payouts are temporarily unavailable. Please contact support for details.*
{
"message": "Technical works."
}
*A transaction with this order_id already exists in your account. Always use a unique order_id.*
{
"message": "order_id exists"
}
*An invalid value was provided in the “service” field.*
{
"message": "Not exists service / No found service"
}
*Creating transactions in this project is not allowed. Please contact support for details.*
{
"message": "Project not allowed to accept payments"
}
*Creating payouts on this account is not allowed. Please contact support for details.*
{
"message": "You are not allowed to accept payouts."
}
*The submitted amount is below the minimum allowed for this service.*
{
"message": "Minimum amount " . $amount
}
*The submitted amount exceeds the maximum allowed for this service.*
{
"message": "Maximum amount " . $amount
}
*Invalid card number provided.*
{
"message": "Invalid card number"
}
*Insufficient funds to complete the payout.*
{
"message": "Insufficient funds"
}
*Payouts to this country are not available for this service.*
{
"message": "Country forbidden"
}
*Payouts to cards issued in this country are not available for this service.*
{
"message": "Card Country forbidden"
}
*The card-issuing bank is not supported for payouts in this service.*
{
"message": "Forbidden bank"
}
500 Server Error — Unexpected server-side error.
An internal error occurred while processing the request. Receiving this HTTP code does not guarantee that a payout has not been created. The system could have created the payout in time, but could not return the correct response. In such a situation, you do not need to consider the payment unsuccessful, delete it, or try to create it again without checking the status. This may lead to duplicate payments. If you receive such an error, you should contact support by attaching the request and response logs to clarify the reason and payment status.
{
"message": "Internal server error"
}
Note: Possible values of payment_status, see the documentation "Payout status".
Important Notes
If you passed notification_endpoint_id, then you will receive a notification about the change in the payout status.
Example Implementation
Unpack the downloaded archive into the folder "path to script/cert/payout/"
function requestPayout(string $method, string $account, array $data)
{
$data = json_encode($data);
$privateKey = openssl_pkey_get_private(
file_get_contents(__DIR__ . '/cert/payout/private.pem'),
file_get_contents(__DIR__ . '/cert/payout/password.txt')
);
if ($privateKey === false) {
throw new \Exception('Error cert.');
}
openssl_sign($data, $sign, $privateKey);
$sign = base64_encode($sign);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api-merchant.alikassa.io/' . $method);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'Account: ' . $account,
'Sign: ' . $sign,
]);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_USERAGENT, 'AliKassa2.0 API');
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
$response = curl_exec($ch);
return json_decode($response, true);
}
$payout = requestPayout('v1/payout', '93d5df06-996c-48c3-9847-348d6b580b80', [
'order_id' => (string)time(),
'amount' => 500,
'number' => '79005554455',
'notification_endpoint_id' => 5,
'service' => 'qiwi_rub',
]);
var_dump($payout);
import { readFileSync } from 'fs';
import { createSign } from 'crypto';
import https from 'https';
async function requestPayout(method, account, data) {
const dataStr = JSON.stringify(data);
const privateKeyPem = readFileSync('cert/payout/private.pem', 'utf8');
const passphrase = readFileSync('cert/payout/password.txt', 'utf8').trim();
const signer = createSign('SHA1');
signer.update(dataStr);
signer.end();
const sign = signer.sign({ key: privateKeyPem, passphrase }, 'base64');
const options = {
hostname: 'api-merchant.alikassa.io',
path: `/${method}`,
method: 'POST',
headers: {
'Content-Type': 'application/json',
Account: account,
Sign: sign,
'User-Agent': 'AliKassa2.0 API',
},
timeout: 30000,
};
return new Promise((resolve, reject) => {
const req = https.request(options, (res) => {
let body = '';
res.on('data', (chunk) => (body += chunk));
res.on('end', () => resolve(JSON.parse(body)));
});
req.on('error', reject);
req.write(dataStr);
req.end();
});
}
(async () => {
const payout = await requestPayout(
'v1/payout',
'93d5df06-996c-48c3-9847-348d6b580b80',
{
order_id: String(Date.now()),
amount: 500,
number: '79005554455',
notification_endpoint_id: 5,
service: 'qiwi_rub',
}
);
console.log(payout);
})();
import json
import base64
import requests
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding
def request_payout(method: str, account: str, data: dict):
data_bytes = json.dumps(data).encode('utf-8')
with open('cert/payout/password.txt', 'rb') as f:
password = f.read().strip()
with open('cert/payout/private.pem', 'rb') as f:
private_key = serialization.load_pem_private_key(f.read(), password=password)
signature = private_key.sign(data_bytes, padding.PKCS1v15(), hashes.SHA1())
sign = base64.b64encode(signature).decode('utf-8')
headers = {
'Content-Type': 'application/json',
'Account': account,
'Sign': sign,
'User-Agent': 'AliKassa2.0 API',
}
resp = requests.post(f'https://api-merchant.alikassa.io/{method}', json=data, headers=headers, timeout=30)
return resp.json()
payout = request_payout('v1/payout', '93d5df06-996c-48c3-9847-348d6b580b80', {
'order_id': str(int(__import__('time').time())),
'amount': 500,
'number': '79005554455',
'notification_endpoint_id':5,
'service': 'qiwi_rub',
})
print(payout)
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.Signature;
import java.util.Base64;
import java.util.Map;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
public class Example {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
Map<String,Object> data = Map.of(
"order_id", String.valueOf(System.currentTimeMillis()),
"amount", 500,
"number", "79005554455",
"notification_endpoint_id",5,
"service", "qiwi_rub"
);
byte[] dataBytes = mapper.writeValueAsBytes(data);
char[] pass = Files.readString(Paths.get("cert/payout/password.txt")).toCharArray();
PEMParser parser = new PEMParser(Files.newBufferedReader(Paths.get("cert/payout/private.pem")));
PEMEncryptedKeyPair ckp = (PEMEncryptedKeyPair) parser.readObject();
var kp = new JcaPEMKeyConverter().getKeyPair(
ckp.decryptKeyPair(new JcePEMDecryptorProviderBuilder().build(pass))
);
Signature sig = Signature.getInstance("SHA1withRSA");
sig.initSign(kp.getPrivate());
sig.update(dataBytes);
String sign = Base64.getEncoder().encodeToString(sig.sign());
HttpRequest request = HttpRequest.newBuilder()
.uri(java.net.URI.create("https://api-merchant.alikassa.io/v1/payout"))
.header("Content-Type", "application/json")
.header("Account", "93d5df06-996c-48c3-9847-348d6b580b80")
.header("Sign", sign)
.header("User-Agent", "AliKassa2.0 API")
.POST(HttpRequest.BodyPublishers.ofString(new String(dataBytes, StandardCharsets.UTF_8)))
.build();
var client = HttpClient.newHttpClient();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(mapper.readValue(response.body(), Object.class));
}
}
package main
import (
"bytes"
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
"io/ioutil"
"net/http"
"time"
"fmt"
)
func signData(data interface{}) string {
dataBytes, _ := json.Marshal(data)
pemBytes, _ := ioutil.ReadFile("cert/payout/private.pem")
passBytes, _ := ioutil.ReadFile("cert/payout/password.txt")
block, _ := pem.Decode(pemBytes)
der, _ := x509.DecryptPEMBlock(block, passBytes)
priv, _ := x509.ParsePKCS1PrivateKey(der)
hash := sha1.Sum(dataBytes)
sigBytes, _ := rsa.SignPKCS1v15(rand.Reader, priv, crypto.SHA1, hash[:])
return base64.StdEncoding.EncodeToString(sigBytes)
}
func requestPayout(method, account string, data map[string]interface{}) (map[string]interface{}, error) {
sign := signData(data)
body, _ := json.Marshal(data)
req, _ := http.NewRequest("POST", "https://api-merchant.alikassa.io/"+method, bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Account", account)
req.Header.Set("Sign", sign)
req.Header.Set("User-Agent", "AliKassa2.0 API")
client := &http.Client{Timeout: 30 * time.Second}
resp, _ := client.Do(req)
defer resp.Body.Close()
var result map[string]interface{}
json.NewDecoder(resp.Body).Decode(&result)
return result, nil
}
func main() {
payout, _ := requestPayout("v1/payout", "93d5df06-996c-48c3-9847-348d6b580b80", map[string]interface{}{
"order_id": time.Now().Unix(),
"amount": 500,
"number": "79005554455",
"notification_endpoint_id": 5,
"service": "qiwi_rub",
})
fmt.Println(payout)
}