Get your API base URL, secret key, client ID, and Base64 encryption key from the FlowPe team. Include all four required headers with every API request.
Run a simulation to see encryption and the documented response in action.
Responses use the unchanged documentation samples. Sample order IDs and amounts are independent of the editable request. Encryption uses a temporary demo key.
1Validate payload
2Encrypt locally
3Sample response
4Decrypt & inspect
The complete API reference
1. API Security
All APIs in this document share common security measures:
All API requests must use HTTPS protocol
All API headers must include "secret-key", "client-id", "X-Request-Id", and "X-Timestamp" X-Timestamp should be in UTC
"X-Request-Id" must contain a unique request identifier, and "X-Timestamp" must contain the request timestamp
Use the API base URL provided by the FlowPe team
Request body format is JSON
Encryption key ENCRYPTION_KEY_BASE64 will be provided by the team
2. Initiate Payment URL API
This API is used to initiate an intent transaction.
Endpoint: POST /tur-core-main/api/v1/external/v1/payments/initiate
Request Parameters
Key
Description
Mandatory
amount
Payment amount as a whole number (e.g., 300)
Yes
orderId
Merchant-provided unique identifier, maximum 32 characters
Yes
returnUrl
Callback URL to redirect the user after payment completion
Yes
username
Customer name
Yes
phoneNumber
Customer phone number, including country code
Yes
email
Customer email address; must be a valid email address
Note: Once the transaction status is updated, the merchant will receive the status. We expect a 200 status code with the message "OK" in response.
Important: Use the provided valid key to decrypt the callback you received.
5. Encryption and Decryption Logic
Requests and responses use AES-256-CBC with a 32-byte key. The key is supplied as Base64 and must decode to exactly 32 bytes. Generate a random 16-byte initialization vector (IV) for each encryption, prepend it to the ciphertext, then Base64-encode the combined bytes. AES-CBC uses PKCS#7 padding (called PKCS5Padding by Java).
Encrypt the plain request JSON, then send it as {"encryptedRequest":"BASE64_VALUE"}. The API returns {"response":"BASE64_VALUE"}; Base64-decode that value, split off the first 16 bytes as the IV, and decrypt the remaining ciphertext to get the original JSON response.
Replace the key placeholder in the examples with your own Base64-encoded key supplied by the API provider. Do not publish or commit real keys or credentials.
Code Examples
The examples contain only encryption and decryption logic, without endpoint-specific request fields. Supply your request JSON separately. Each example uses the same IV-prefixed format.
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class EncryptionExample {
private static final String KEY_BASE64 = "YOUR_BASE64_ENCODED_32_BYTE_KEY";
private static final int IV_LENGTH = 16;
public static void main(String[] args) {
try {
if (args.length > 0) {
System.out.println(encrypt(args[0], KEY_BASE64));
}
if (args.length > 1) {
System.out.println(decrypt(args[1], KEY_BASE64));
}
} catch (Exception e) {
System.err.println("Encryption or decryption failed: " + e.getMessage());
}
}
public static String encrypt(String plainText, String base64Key) throws Exception {
byte[] key = decodeKey(base64Key);
byte[] iv = new byte[IV_LENGTH];
new SecureRandom().nextBytes(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv));
byte[] ciphertext = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
byte[] packed = new byte[iv.length + ciphertext.length];
System.arraycopy(iv, 0, packed, 0, iv.length);
System.arraycopy(ciphertext, 0, packed, iv.length, ciphertext.length);
return Base64.getEncoder().encodeToString(packed);
}
public static String decrypt(String encryptedBase64, String base64Key) throws Exception {
byte[] packed = Base64.getDecoder().decode(encryptedBase64);
if (packed.length <= IV_LENGTH || (packed.length - IV_LENGTH) % 16 != 0) {
throw new IllegalArgumentException("Invalid encrypted data length");
}
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(decodeKey(base64Key), "AES"),
new IvParameterSpec(packed, 0, IV_LENGTH));
byte[] decrypted = cipher.doFinal(packed, IV_LENGTH, packed.length - IV_LENGTH);
return new String(decrypted, StandardCharsets.UTF_8);
}
private static byte[] decodeKey(String base64Key) {
byte[] key = Base64.getDecoder().decode(base64Key);
if (key.length != 32) {
throw new IllegalArgumentException("Key must decode to 32 bytes");
}
return key;
}
}
const KEY_BASE64 = 'YOUR_BASE64_ENCODED_32_BYTE_KEY';
const toBytes = (base64) => Uint8Array.from(atob(base64), c => c.charCodeAt(0));
const toBase64 = (bytes) => btoa(Array.from(bytes, b => String.fromCharCode(b)).join(''));
async function encryptAES(plainText, base64Key) {
const keyBytes = toBytes(base64Key);
if (keyBytes.length !== 32) throw new Error('AES-256 key must decode to 32 bytes');
const key = await crypto.subtle.importKey('raw', keyBytes, 'AES-CBC', false, ['encrypt']);
const iv = crypto.getRandomValues(new Uint8Array(16));
const ciphertext = new Uint8Array(await crypto.subtle.encrypt(
{ name: 'AES-CBC', iv }, key, new TextEncoder().encode(plainText)
));
const packed = new Uint8Array(iv.length + ciphertext.length);
packed.set(iv);
packed.set(ciphertext, iv.length);
return toBase64(packed);
}
async function decryptAES(encryptedBase64, base64Key) {
const packed = toBytes(encryptedBase64);
const keyBytes = toBytes(base64Key);
if (keyBytes.length !== 32) throw new Error('AES-256 key must decode to 32 bytes');
if (packed.length <= 16 || (packed.length - 16) % 16 !== 0) {
throw new Error('Invalid encrypted data length');
}
const key = await crypto.subtle.importKey('raw', keyBytes, 'AES-CBC', false, ['decrypt']);
const decrypted = await crypto.subtle.decrypt(
{ name: 'AES-CBC', iv: packed.slice(0, 16) }, key, packed.slice(16)
);
return new TextDecoder().decode(decrypted);
}