PAYUNITAPI
Merchant API

A simple payment integration from one request.

Your server creates a payment intent and receives a checkout_url. Redirect the customer, then track status through the API or a webhook.

Base URL: https://payunit.co/api/v1
Send the API key from your server only. Never expose it in browser JavaScript or a client application.

Amount format

All API amounts are decimal USD strings, not cents. Use at most two decimal places.

"1"

$1.00

One dollar

"1.75"

$1.75

One dollar and 75 cents

"74.00"

$74.00

Seventy-four dollars

POST /payment-intents

Send Authorization and a unique Idempotency-Key for each commercial operation. Reusing the same key with the same body returns the same intent.

curl -X POST "https://payunit.co/api/v1/payment-intents" \
  -H "Authorization: Bearer pay_live_YOUR_KEY" \
  -H "Idempotency-Key: order-1048" \
  -H "Content-Type: application/json" \
  --data '{
    "amount": "74.00",
    "currency": "USD",
    "external_ref": "ORDER-1048",
    "description": "Order payment",
    "success_url": "https://merchant.example/success",
    "cancel_url": "https://merchant.example/cancel",
    "methods": ["crypto", "togo", "lahza"]
  }'

PHP

<?php
$payload = [
    'amount' => '74.00',
    'currency' => 'USD',
    'external_ref' => 'ORDER-1048',
    'success_url' => 'https://merchant.example/success',
    'cancel_url' => 'https://merchant.example/cancel',
    'methods' => ['crypto', 'togo', 'lahza'],
];

$ch = curl_init('https://payunit.co/api/v1/payment-intents');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer pay_live_YOUR_KEY',
        'Idempotency-Key: order-1048',
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode($payload, JSON_THROW_ON_ERROR),
]);
$response = curl_exec($ch);
if ($response === false) {
    throw new RuntimeException(curl_error($ch));
}
$data = json_decode($response, true, 32, JSON_THROW_ON_ERROR);
header('Location: ' . $data['checkout_url']);

Minimal response

{
  "id": "pi_live_...",
  "status": "requires_payment_method",
  "amount": "74.00",
  "checkout_url": "https://payunit.co/checkout/..."
}

GET /payment-intents/{id}

Use the intent ID to retrieve current status, amount, fees, and payment method. Do not rely only on the browser return URL to fulfil an order.

curl "https://payunit.co/api/v1/payment-intents/pi_live_EXAMPLE" \
  -H "Authorization: Bearer pay_live_YOUR_KEY"

Webhooks

PAYUNIT sends a signed JSON event to the configured HTTPS URL. Verify X-Payunit-Signature with HMAC-SHA256 over timestamp + "." + the raw request body.

$raw = file_get_contents('php://input');
$header = $_SERVER['HTTP_X_PAYUNIT_SIGNATURE'] ?? '';
parse_str(str_replace(',', '&', $header), $parts);
$timestamp = (string)($parts['t'] ?? '');
$received = (string)($parts['v1'] ?? '');
$expected = hash_hmac('sha256', $timestamp . '.' . $raw, $webhookSecret);

if ($timestamp === '' || !hash_equals($expected, $received)) {
    http_response_code(401);
    exit;
}
$event = json_decode($raw, true, 32, JSON_THROW_ON_ERROR);

Verify the signature, prevent event replay, and return HTTP 200 quickly after persisting the event. Run heavy processing separately.

Payment statuses

StatusMeaning
requires_payment_methodCreated; the customer has not selected a method yet.
processingPayment started and final confirmation is pending.
succeededPayment succeeded and the order can be fulfilled.
failedThe payment attempt failed.
cancelledThe payment was cancelled.
expiredThe checkout session expired.

Operational security

Server-side key

Never expose the API key in browser or app code.

Idempotency

Use one stable key per order to prevent duplicate payment intents.

Verify status

Fulfil only after succeeded from the API or a verified webhook.

Prefer a ready integration?

Use one of the ready packages.

View integrations