Kommo + Checkout.com: Payment Gateway from Your Pipeline
When a deal moves to “Won”, Kommo automatically creates a payment link via the Checkout.com API and saves it to the deal card. The sales rep sends the link to the client with one click, and the payment status comes back into the CRM via webhook - no manual steps required.
In projects with EU e-commerce and fintech companies on Kommo, we see the same pattern over and over: when a deal is won, the manager manually opens the Checkout.com dashboard, creates a payment link, copies it into the CRM card, and then manually tracks whether payment went through. At 50-80 deals per month, that’s 2-3 hours of routine work every week, plus a constant risk of error - wrong amount, wrong contact, lost link. Checkout.com holds a strong position in the EU market precisely because it supports Strong Customer Authentication (SCA) as required by PSD2 and has local acquiring relationships in key countries. This makes it the first choice for fintech and e-commerce companies serving European buyers. This article covers the integration architecture, working Python code, and a real case study with numbers.
Why there is no native Checkout.com integration with Kommo
Kommo offers native connections to Stripe and PayPal via its widget marketplace, but Checkout.com is not there. The reason is not technical complexity but market logic: Checkout.com targets enterprise and mid-market clients with custom processes, not low-code Zapier integrations.
If you try to solve this through Zapier or Make, you immediately hit limitations: no ready-made Checkout.com module with Payment Links API v2 support, no HMAC signature handling for webhook events, no idempotency logic for retries. All of this has to be built manually, and at that point it is simpler to write a proper Python service from the start.
Payment Links API is a Checkout.com endpoint for generating hosted payment pages without embedding a form on your site. The link opens on Checkout.com’s infrastructure, supports 3D Secure, and returns webhook events with the payment result.
Integration architecture
The integration works in two directions:
-
Kommo -> Checkout.com: when a deal moves to “Won”, Kommo sends a webhook to your service. The service fetches the deal data, creates a Payment Link via the Checkout.com API, and writes the URL back into a custom field on the deal card.
-
Checkout.com -> Kommo: when the buyer pays (or declines), Checkout.com sends a webhook event to your service. The service updates the deal status and adds a note with transaction details.
Technical stack:
- Python 3.11 + FastAPI for the webhook handler
- Checkout.com Secret Key for Bearer authorization
- HMAC-SHA256 for verifying signatures of incoming webhooks
- Kommo API for updating deal fields
The reference field in the Payment Links API stores the Kommo Deal ID - this is the link between the payment and the deal in the CRM.
import hmac
import hashlib
import httpx
from fastapi import FastAPI, Request, HTTPException
from pydantic import BaseModel
app = FastAPI()
CKO_SECRET_KEY = "sk_sbox_xxx" # Checkout.com Secret Key
CKO_WEBHOOK_SECRET = "your_webhook_secret" # from Checkout.com dashboard
KOMMO_SUBDOMAIN = "your_company"
KOMMO_TOKEN = "your_kommo_long_lived_token"
def verify_cko_signature(payload: bytes, signature_header: str) -> bool:
"""Verify HMAC-SHA256 signature from Checkout.com."""
expected = hmac.new(
CKO_WEBHOOK_SECRET.encode(),
payload,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature_header)
async def create_payment_link(deal_id: int, amount: int, currency: str, contact_email: str) -> str:
"""Creates a Payment Link in Checkout.com and returns the URL."""
async with httpx.AsyncClient() as client:
response = await client.post(
"https://api.checkout.com/payment-links",
headers={
"Authorization": f"Bearer {CKO_SECRET_KEY}",
"Content-Type": "application/json"
},
json={
"amount": amount, # in smallest currency units (cents)
"currency": currency, # "EUR", "GBP", "USD"
"reference": str(deal_id), # Kommo Deal ID as reference
"description": f"Invoice #{deal_id}",
"customer": {"email": contact_email},
"3ds": {"enabled": True}, # SCA for EU
"expires_in": 86400 # 24 hours
},
timeout=10.0
)
response.raise_for_status()
return response.json()["_links"]["redirect"]["href"]
async def update_kommo_deal(deal_id: int, payment_url: str, field_id: int):
"""Writes the Payment Link URL into a custom deal field in Kommo."""
async with httpx.AsyncClient() as client:
await client.patch(
f"https://{KOMMO_SUBDOMAIN}.kommo.com/api/v4/leads/{deal_id}",
headers={"Authorization": f"Bearer {KOMMO_TOKEN}"},
json={"custom_fields_values": [{"field_id": field_id, "values": [{"value": payment_url}]}]},
timeout=10.0
)
@app.post("/kommo/webhook")
async def handle_kommo_webhook(request: Request):
"""Handles webhook from Kommo on deal status change."""
data = await request.json()
# Check that this is a deal won event
leads = data.get("leads", {}).get("status", [])
for lead in leads:
if lead.get("status_id") == 142: # Won status ID in your pipeline
deal_id = lead["id"]
# Fetch amount and email from deal fields here
# (simplified for the example)
payment_url = await create_payment_link(
deal_id=deal_id,
amount=lead.get("price", 0) * 100, # convert to cents
currency="EUR",
contact_email=lead.get("contact_email", "")
)
await update_kommo_deal(deal_id, payment_url, field_id=12345)
return {"status": "ok"}
@app.post("/checkout/webhook")
async def handle_checkout_webhook(request: Request):
"""Handles incoming webhook events from Checkout.com."""
body = await request.body()
signature = request.headers.get("cko-signature", "")
if not verify_cko_signature(body, signature):
raise HTTPException(status_code=401, detail="Invalid signature")
event = await request.json()
event_type = event.get("type")
deal_id = int(event["data"]["reference"]) # our Kommo Deal ID
if event_type == "payment_captured":
# Payment successful - move the deal or add a note
await update_kommo_deal_note(deal_id, f"Payment received: {event['data']['amount']} {event['data']['currency']}")
elif event_type == "payment_declined":
await update_kommo_deal_note(deal_id, f"Payment declined: {event['data']['response_summary']}")
return {"status": "ok"}
Step-by-step implementation
Step 1. Checkout.com setup
In the Checkout.com Dashboard, go to Settings -> Channels. Copy the Secret Key (starts with sk_). In the Webhooks section, create an endpoint, enter your service URL, and enable events: payment_captured, payment_declined, payment_approved. Save the Webhook Secret for HMAC verification.
Step 2. Custom field in Kommo
In Kommo, create a custom “Text” field named “Payment Link” (note its field_id from the API). This field will store the link to send to the client.
Step 3. Webhook from Kommo
In Kommo settings (Settings -> Notifications), add a webhook pointing to /kommo/webhook on your service. Set the trigger to “Deal: stage changed” for the “Won” stage.
Step 4. Deal field mapping
From the Kommo webhook, extract the deal’s price (contract amount) and the contact’s email via an API call to /api/v4/leads/{id}?with=contacts. Take the currency from a custom field or set a default.
Step 5. 3D Secure and SCA
For EU transactions above EUR 30, include "3ds": {"enabled": true} in the Payment Links API request body. Checkout.com automatically applies Strong Customer Authentication wherever PSD2 requires it.
Step 6. Exponential backoff
The Checkout.com API returns 429 when rate limits are exceeded. Implement retries with delays of 1s, 2s, 4s, 8s (maximum 4 attempts) before logging the error and notifying the team.
Real case: EU e-commerce, 65 deals per month
Client - an EU e-commerce company selling B2B through Kommo. Before the integration: the manager manually created a payment link in Checkout.com after each confirmation call, copied the URL into the card, and wrote an email to the client. Average time per operation - 8-12 minutes.
After the integration: the Payment Link is created automatically when the deal moves to “Won”, and the URL appears in the card within 2-3 seconds. The manager sees the link and copies it to WhatsApp or email in one click. Payment status (captured/declined) arrives in the CRM automatically.
Results after the first month:
- Time saved: ~7 hours per month (65 deals x 6 minutes)
- Amount mapping errors: dropped from 4-5 per month to zero
- Conversion from “Won” to “Paid” increased by 11% - due to speed: the link reaches the client while they are still on the call, not 30 minutes later
Additional benefit: the sales director gained the ability to track the payment pipeline directly in Kommo without exporting from the Checkout.com dashboard.
Who this integration is right for
This approach works best for companies with several characteristics:
- Use Kommo as their primary sales tool
- Accept payments from EU buyers (SCA support matters)
- Close 30+ deals per month - below that volume, manual work is not critical
- Average deal value above $500 - with smaller amounts the administrative burden is felt more keenly
- No dedicated billing team that handles invoice creation independently
If you already use Checkout.com and are building custom integrations for Kommo, this approach fits into your existing stack without replacing any tools.
Frequently asked questions
Can this integration handle payments in multiple currencies?
Yes. The Payment Links API accepts a currency parameter on each link creation request. In Kommo, create a custom field “Deal Currency” and pass its value in the API call. Checkout.com supports more than 150 currencies. Exchange rate conversion happens on Checkout.com’s side when multicurrency acquiring is enabled.
What happens if a webhook from Checkout.com arrives late or is lost?
Checkout.com retries webhook delivery up to 10 times with exponential backoff over 24 hours. You can also set up polling via the Events API - query events by reference (Deal ID) every 15-30 minutes to reconcile statuses. For critical transactions we recommend combining both approaches.
How do I verify the webhook signature if we are not using Python?
The algorithm is the same in any language: take the raw request body as bytes, compute HMAC-SHA256 with your Webhook Secret, and compare with the cko-signature header. In Node.js that is crypto.createHmac('sha256', secret).update(rawBody).digest('hex'). Important: work with the raw body, not parsed JSON - otherwise field order may change.
Does Checkout.com support Apple Pay and Google Pay via payment links?
As of mid-2026 - yes, if you have Apple Pay / Google Pay configured in your Checkout.com account. A buyer opening the Payment Link on mobile will automatically see available wallets. No additional API-level configuration is required for this.
Is a dedicated server needed for the webhook handler?
No. A $5-10/month VPS or a serverless function (AWS Lambda, Google Cloud Functions) is sufficient. The load is minimal: even at 200 deals per month that is fewer than 20 requests per day. The FastAPI service uses less than 128 MB of RAM.
If you close 30+ deals per month through Checkout.com and your team spends time manually creating payment links - describe the task to the Exceltic.dev team. We will review the architecture for your stack and estimate the scope of work.