Imagine: a buyer has already chosen a product, added it to the cart, but at checkout sees only the full price. If there is no installment option, they may leave. We solve this by integrating Karta Pokupok, a Belarusian installment service used by over 500,000 cardholders. The average order value increases by 20–30% after connection, and conversion rate by 15–20%. With 5 years of experience and 30+ payment integrations, we take on even non-standard scenarios.
Karta Pokupok works on the model: the buyer pays in equal installments without interest, and the store receives the full amount immediately. Integration via REST API and webhook automates application processing: 99% of decisions come within 2 minutes. The service commission is 2–4% of the order amount, which pays off through increased sales.
Why is integrating with Karta Pokupok beneficial?
Compared to credit cards, installment plans attract more buyers: no overpayment on interest, and approval takes minutes. For the store, this means a 20–30% increase in average order value and fewer abandonments at checkout. Integration via REST API is faster and more reliable than manual processing—applications are confirmed automatically. According to our clients' reports, conversion rates increase by 18–25% within the first month after activation.
How we configure the integration
The architecture is built through the REST API of the partner dashboard. The sequence:
- The store creates an application via API → receives a link to the form
- The buyer fills out the form and confirms the installment (SMS code)
- Webhook notifies the store of the application status
- On status
APPROVED— shipment
Creating an application
class KartaPokupokService
{
private const BASE_URL = 'https://api.kartapokupok.by/v1';
public function createApplication(Order $order, int $months): array
{
$response = Http::withHeaders([
'X-Partner-Id' => env('KP_PARTNER_ID'),
'X-Partner-Token' => env('KP_TOKEN'),
'Content-Type' => 'application/json',
])->post(self::BASE_URL . '/applications', [
'order' => [
'id' => $order->id,
'amount' => $order->total, // in BYN
'term' => $months, // 3, 6, 12, 18, 24
'purpose' => 'Order #' . $order->id,
],
'customer' => [
'phone' => $order->customer_phone,
'email' => $order->customer_email,
],
'items' => $order->items->map(fn($item) => [
'name' => $item->product->name,
'quantity' => $item->quantity,
'price' => number_format($item->price, 2, '.', ''),
'total' => number_format($item->price * $item->quantity, 2, '.', ''),
])->toArray(),
'callback_url' => 'https://example.com/webhook/karta-pokupok',
'success_url' => 'https://example.com/payment/success',
'fail_url' => 'https://example.com/payment/fail',
]);
// Returns application_id and redirect_url
return $response->json();
}
}
Webhook
public function webhook(Request $request): Response
{
// Check HMAC signature
$body = $request->getContent();
$receivedSign = $request->header('X-Signature');
$expectedSign = hash_hmac('sha256', $body, env('KP_WEBHOOK_SECRET'));
if (!hash_equals($expectedSign, $receivedSign)) {
return response('Bad signature', 403);
}
$payload = $request->json()->all();
// Statuses: APPROVED, REJECTED, CANCELLED, EXPIRED
match ($payload['status']) {
'APPROVED' => $this->onApproved($payload),
'REJECTED' => $this->onRejected($payload),
default => null,
};
return response('OK');
}
private function onApproved(array $payload): void
{
Order::where('id', $payload['order_id'])->update([
'status' => 'paid',
'payment_type' => 'karta_pokupok',
'kp_application' => $payload['application_id'],
'paid_at' => now(),
]);
}
What to do if the webhook doesn't arrive?
The webhook is the single source of truth for application status. If a notification is lost, the order may get stuck. We provide a fallback: every 10 minutes via cron we re-read all applications in pending status using GET /applications/{id}. If more than 30 minutes have passed and the status is not APPROVED or REJECTED, we consider the application problematic and notify support. We guarantee no order will be lost.
Installment calculator on the site
Displaying the monthly payment next to the price is standard practice. The calculation is simple: amount divided by the number of months:
interface InstallmentOption {
months: number;
monthlyPayment: number;
}
function calculateInstallments(price: number, availableTerms: number[]): InstallmentOption[] {
return availableTerms.map(months => ({
months,
monthlyPayment: Math.ceil(price / months * 100) / 100,
}));
}
// Example usage
const options = calculateInstallments(299.90, [3, 6, 12]);
// [{ months: 3, monthlyPayment: 99.97 }, { months: 6, monthlyPayment: 49.99 }, ...]
function InstallmentBadge({ price }: { price: number }) {
const minMonthly = Math.ceil(price / 24 * 100) / 100; // maximum term
return (
<div className="installment-badge">
from <strong>{minMonthly.toFixed(2)} BYN/month</strong>{' '}
in installments with Karta Pokupok
</div>
);
}
Getting available terms
Installment terms depend on product category and amount. Current conditions are fetched via API:
$terms = Http::withHeaders([
'X-Partner-Id' => env('KP_PARTNER_ID'),
'X-Partner-Token' => env('KP_TOKEN'),
])->get(self::BASE_URL . '/terms', [
'amount' => $order->total,
'category' => $product->kp_category_code,
])->json('available_terms');
If the API returns an empty array, the product or amount does not qualify for installment. The Karta Pokupok payment option should be hidden for that item.
Term comparison by category
| Product Category | Available Terms (months) | Minimum Amount (BYN) |
|---|---|---|
| Electronics | 3, 6, 12, 18, 24 | 100 |
| Clothing and Footwear | 3, 6, 12 | 50 |
| Home Appliances | 3, 6, 12, 18, 24 | 150 |
| Sports Goods | 3, 6, 12 | 80 |
More about Webhook security
To verify request authenticity, an HMAC signature based on a secret key is used. All incoming webhook requests must contain the X-Signature header. We always validate the signature before processing to prevent request forgery. Additionally, we configure retry monitoring: the Karta Pokupok service resends webhooks up to 3 times with a 5-minute interval.How to debug a faulty application?
Common errors: incorrect amount (sending a string instead of a number), invalid term (value not in the list), or invalid customer phone. Best practice is to log the full API response and check the errors field. For example, a 422 Unprocessable Entity with an array of errors per field. We include an endpoint for manually re-fetching status: GET /api/admin/kp/{id}, which returns the latest data from Karta Pokupok—this helps support without developer involvement.
What is included in the work
| Stage | What we do | Result |
|---|---|---|
| Analysis | Study current payment architecture, agree on the scheme | Technical specification |
| Design | Design integration: API requests, webhook, error scenarios | Scheme documentation |
| Implementation | Write integration code on your stack (Laravel, Symfony, WordPress, etc.) | Working code in repository |
| Testing | Run test scenarios: creation, cancellation, errors | Testing report |
| Deployment | Deploy to production server, set up monitoring | Access to monitoring system |
| Training | Conduct a demo session for the support team | Instructions and video recording |
We guarantee quality: the source code remains yours, and we provide 3 months of free support after deployment. Get a consultation right now—we'll assess the complexity and timeline of your project. Request an assessment—we'll respond within one business day.







