Cards#
CentaPay supports card-present-not-present (CNP) transactions across Kazakhstan and Uzbekistan.
SALE — Single Message (SMS)#
Authorise and capture in one step. This is the standard flow for immediate payments.
https://{PAYMENT_URL}/post
curl -X POST https://{PAYMENT_URL}/post \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "action=SALE" \
-d "client_key={CLIENT_KEY}" \
-d "order_id=ORD-001" \
-d "order_amount=5000" \
-d "order_currency=KZT" \
-d "order_description=Product+purchase" \
-d "card_number=4111111111111111" \
-d "card_exp_month=01" \
-d "card_exp_year=2038" \
-d "card_cvv2=000" \
-d "payer_first_name=John" \
-d "payer_last_name=Smith" \
-d "[email protected]" \
-d "payer_phone=77001234567" \
-d "payer_country=KZ" \
-d "payer_city=Astana" \
-d "payer_address=10+Kunayeva+St" \
-d "payer_zip=010000" \
-d "payer_ip=192.168.1.1" \
-d "term_url_3ds=https://yoursite.com/3ds-return" \
-d "hash={HASH}"$card = '4111111111111111'; $params = [ 'action' => 'SALE', 'client_key' => CLIENT_KEY, 'order_id' => 'ORD-001', 'order_amount' => '5000', 'order_currency' => 'KZT', 'order_description'=> 'Product purchase', 'card_number' => $card, 'card_exp_month' => '01', 'card_exp_year' => '2038', 'card_cvv2' => '000', 'payer_first_name' => 'John', 'payer_last_name' => 'Smith', 'payer_email' => '[email protected]', 'payer_phone' => '77001234567', 'payer_country' => 'KZ', 'payer_city' => 'Astana', 'payer_address' => '10 Kunayeva St', 'payer_zip' => '010000', 'payer_ip' => $_SERVER['REMOTE_ADDR'], 'term_url_3ds' => 'https://yoursite.com/3ds-return', ]; // Formula 1 hash $cp = substr($card,0,6).substr($card,-4); $params['hash'] = md5(strtoupper( strrev($params['payer_email']).PASSWORD.strrev($cp) )); $ch = curl_init(PAYMENT_URL.'/post'); curl_setopt_array($ch,[ CURLOPT_POST=>true, CURLOPT_POSTFIELDS=>http_build_query($params), CURLOPT_RETURNTRANSFER=>true, ]); $result = json_decode(curl_exec($ch),true);
import requests, hashlib card = '4111111111111111' email = '[email protected]' cp = card[:6] + card[-4:] h = hashlib.md5((email[::-1] + PASSWORD + cp[::-1]).upper().encode()).hexdigest() resp = requests.post(f'{PAYMENT_URL}/post', data={ 'action': 'SALE', 'client_key': CLIENT_KEY, 'order_id': 'ORD-001', 'order_amount': '5000', 'order_currency': 'KZT', 'order_description': 'Product purchase', 'card_number': card, 'card_exp_month': '01', 'card_exp_year': '2038', 'card_cvv2': '000', 'payer_first_name': 'John', 'payer_last_name': 'Smith', 'payer_email': email, 'payer_phone': '77001234567', 'payer_country': 'KZ', 'payer_city': 'Astana', 'payer_address': '10 Kunayeva St', 'payer_zip': '010000', 'payer_ip': '192.168.1.1', 'term_url_3ds': 'https://yoursite.com/3ds-return', 'hash': h, })
const crypto = require('crypto');
const axios = require('axios');
const qs = require('qs');
const rev = s => s.split('').reverse().join('');
const card = '4111111111111111';
const email = '[email protected]';
const cp = card.slice(0,6) + card.slice(-4);
const hash = crypto.createHash('md5')
.update((rev(email) + PASSWORD + rev(cp)).toUpperCase())
.digest('hex');
const {data} = await axios.post(`${PAYMENT_URL}/post`,
qs.stringify({
action:'SALE', client_key:CLIENT_KEY,
order_id:'ORD-001', order_amount:'5000',
order_currency:'KZT', order_description:'Product purchase',
card_number:card, card_exp_month:'01',
card_exp_year:'2038', card_cvv2:'000',
payer_first_name:'John', payer_last_name:'Smith',
payer_email:email, payer_phone:'77001234567',
payer_country:'KZ', payer_city:'Astana',
payer_address:'10 Kunayeva St', payer_zip:'010000',
payer_ip:req.ip,
term_url_3ds:'https://yoursite.com/3ds-return',
hash
}));Currencies#
| Country | Currency | Code | Amount Format |
|---|---|---|---|
| 🇰🇿 Kazakhstan | Kazakhstani Tenge | KZT | Integer — e.g. 5000 |
| 🇺🇿 Uzbekistan | Uzbekistani Som | UZS | Integer — e.g. 120000 |
| International | US Dollar | USD | Float XX.XX — e.g. 49.99 |
AUTH & CAPTURE — Dual Message (DMS)#
Authorise only (hold funds), then capture separately. Add auth=Y to the SALE request. For AUTH, you can set order_amount to 0 for card validation / tokenisation only.
To capture, send a CAPTURE request with the trans_id from the AUTH response. Partial capture is supported (once only). See CAPTURE reference.
status: PENDING. The funds are held on the cardholder's account. You must CAPTURE within the hold period (typically 7 days, varies by issuer) or the hold expires.Tokenisation#
Request a card token on the first transaction by adding req_token=Y to the SALE request. The response and callback return a card_token you can store and reuse for future charges without transmitting card data again.
To charge using a stored token, send card_token instead of card_number, card_exp_month, card_exp_year, card_cvv2. The hash formula changes — use the card_token variant of Formula 1.
Sub-Merchants#
Use the channel_id parameter to route transactions to specific sub-merchants. This value is echoed in callbacks and available in reporting, enabling you to reconcile at sub-merchant level.