Integration guide

Connect a Telegram bot or a website.

Follow the steps for your platform. Every step shows what to click and the exact code to run.

Step 0 · Setup (both platforms)

1

Add your website in the admin panel

Open Admin → log in with your admin token → Websites → Add website. A Telegram bot is also a “website” here — use your bot’s name and any URL (for example https://t.me/your_bot).

Fill in the Webhook URL (where your bot or site receives payment confirmations) and tick the webhook fields you need. Tick Metadata (and External order ID) so you can tell which customer paid.

2

Copy your three secrets

After saving you see a Live API key (pk_primepay_…), a Sandbox key (pk_test_…) and a Webhook secret (whsec_…). You can copy them again any time from the Websites table.

3

Store them on your server

Put them in environment variables on the machine that runs your bot or website backend — never in browser code, an app, or a public repository.

# .env on your bot / website server  (never commit this file, never put it in browser JavaScript)
PRIME_PAY_URL=https://your-prime-pay-domain.com
PRIME_PAY_API_KEY=pk_test_xxxxxxxxxxxxxxxx        # sandbox first, swap for pk_primepay_... when going live
PRIME_PAY_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxxxx
BOT_TOKEN=123456:ABC-your-telegram-bot-token         # Telegram bots only
4

Try it in the API Playground

Admin → API Playground, choose your website’s sandbox key, enter an amount and press Send request. You should see HTTP 200 and a checkout_url. Use Test webhook next to the site to confirm your endpoint answers with HTTP 200.

Telegram bot

1

Create the bot

In Telegram open @BotFather → /newbot → copy the bot token into BOT_TOKEN.

2

Run the bot + webhook receiver

The code below does both jobs in one program: it shows products, creates a Prime Pay checkout when a customer taps one, and receives the signed webhook to confirm payment.

# pip install pyTelegramBotAPI flask requests
import os, time, json, hmac, hashlib, threading
import requests, telebot
from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton
from flask import Flask, request, abort

PRIME_PAY = os.environ["PRIME_PAY_URL"].rstrip("/")          # https://your-prime-pay-domain
API_KEY   = os.environ["PRIME_PAY_API_KEY"]                   # use pk_test_... while testing
SECRET    = os.environ["PRIME_PAY_WEBHOOK_SECRET"]            # whsec_...
bot = telebot.TeleBot(os.environ["BOT_TOKEN"])
app = Flask(__name__)

PRODUCTS = {"d520": ("520 Diamonds", 499), "elite": ("Elite Pass", 149)}
delivered = set()                                             # use a database in production

@bot.message_handler(commands=["start", "buy"])
def buy(m):
    kb = InlineKeyboardMarkup()
    for pid, (name, price) in PRODUCTS.items():
        kb.add(InlineKeyboardButton(f"{name} - Rs {price}", callback_data=f"buy:{pid}"))
    bot.send_message(m.chat.id, "Choose a product:", reply_markup=kb)

@bot.callback_query_handler(func=lambda c: c.data.startswith("buy:"))
def create_order(c):
    pid = c.data.split(":")[1]
    name, price = PRODUCTS[pid]
    ref = f"TG-{c.from_user.id}-{int(time.time())}"
    r = requests.post(f"{PRIME_PAY}/api/v1/checkout", timeout=15,
        headers={"x-prime-pay-key": API_KEY},
        json={"amount": price, "item_name": name, "external_order_id": ref, "idempotency_key": ref,
              "customer_name": c.from_user.first_name,
              "metadata": {"chat_id": c.message.chat.id, "product_id": pid}})
    data = r.json()
    if r.status_code != 200:
        return bot.answer_callback_query(c.id, data.get("error", "Payment error"), show_alert=True)
    kb = InlineKeyboardMarkup()
    kb.add(InlineKeyboardButton("Pay now", url=data["checkout_url"]))
    bot.send_message(c.message.chat.id, f"{name} - Rs {data['order']['amount']}\nTap below to pay. Your item arrives here automatically.", reply_markup=kb)
    bot.answer_callback_query(c.id)

@app.post("/payment/webhook")
def webhook():
    raw = request.get_data()
    sig = request.headers.get("x-prime-pay-signature", "")
    expected = hmac.new(SECRET.encode(), raw, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(sig, expected):
        abort(401)
    ev = json.loads(raw)
    if ev.get("event") == "payment.verified" and ev["order_code"] not in delivered:
        delivered.add(ev["order_code"])
        chat_id = (ev.get("metadata") or {}).get("chat_id")      # needs "Metadata" ticked in the site's webhook fields
        if chat_id:
            bot.send_message(chat_id, f"Payment received! Order {ev['order_code']}. Delivering your item...")
            # TODO: deliver the product here (send code / activate plan / call your own API)
    return "ok"

if __name__ == "__main__":
    threading.Thread(target=bot.infinity_polling, daemon=True).start()
    app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 8000)))
3

Make the webhook reachable

Deploy the program somewhere with a public HTTPS URL (Render, Railway, a VPS…). Set the website’s Webhook URL in admin to https://your-bot-host/payment/webhook. Prime Pay refuses webhook URLs that point to private or internal addresses.

4

Test with the sandbox key

Start with PRIME_PAY_API_KEY=pk_test_…. Send /start to your bot and tap a product: the order is created already verified (no real money), and your webhook fires immediately with is_test: true so you can test the delivery message.

5

Go live

Replace the key with your Live key and restart. A real order shows the QR checkout; once the customer pays, Prime Pay matches the payment and your bot receives payment.verified.

Website

1

Create the checkout from your backend

When the customer presses “Buy”, call POST /api/v1/checkout from your server and redirect the customer to the returned checkout_url. Always save your own order ID first and send it as external_order_id.

// npm i express        (set "type": "module" in package.json)
import express from "express";
import crypto from "node:crypto";
const app = express();
const { PRIME_PAY_URL, PRIME_PAY_API_KEY, PRIME_PAY_WEBHOOK_SECRET } = process.env;

// 1) Customer clicks "Buy" -> create the checkout on your SERVER and redirect
app.post("/buy", express.json(), async (req, res) => {
  const orderId = "ORDER-" + Date.now();                        // save this against the user in YOUR database first
  const r = await fetch(`${PRIME_PAY_URL}/api/v1/checkout`, {
    method: "POST",
    headers: { "Content-Type": "application/json", "x-prime-pay-key": PRIME_PAY_API_KEY },
    body: JSON.stringify({ amount: 499, item_name: "520 Diamonds", external_order_id: orderId, idempotency_key: orderId,
      success_url: "https://yoursite.com/payment/success", cancel_url: "https://yoursite.com/payment/cancel",
      metadata: { user_id: req.body.userId } })
  });
  const data = await r.json();
  if (!r.ok) return res.status(502).json({ error: data.error });
  res.json({ checkout_url: data.checkout_url });                  // front-end: window.location = checkout_url
});

// 2) Prime Pay calls this when the payment is verified
app.post("/api/payment/webhook", express.raw({ type: "application/json" }), (req, res) => {
  const sig = req.get("x-prime-pay-signature") || "";
  const expected = crypto.createHmac("sha256", PRIME_PAY_WEBHOOK_SECRET).update(req.body).digest("hex");
  if (sig.length !== expected.length || !crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) return res.sendStatus(401);
  const ev = JSON.parse(req.body);
  if (ev.event === "payment.verified") {
    // look up your order by ev.external_order_id (tick "External order ID" in webhook fields) and mark it paid ONCE
  }
  res.send("ok");
});
app.listen(3000);
2

Receive the webhook

Set the website’s Webhook URL in admin (for example https://yoursite.com/api/payment/webhook). Verify the HMAC signature on the raw request body, mark the order paid once, then deliver.

3

Show a success page

Set Success URL. After verification the checkout page sends the customer there with ?order_code=…&status=verified. Do not deliver based on this redirect alone — wait for the webhook.

4

Sandbox, then live

Develop with the pk_test_… key (instant “paid” orders and test webhooks), then swap in the live key.

Webhook reference

Prime Pay signs the exact raw body with your webhook secret (HMAC-SHA256, hex) and sends it as x-prime-pay-signature. If your endpoint does not answer with HTTP 2xx within 10 seconds, delivery is retried automatically with exponential backoff (about 30s, 1m, 2m, 4m… up to 8 attempts). See attempts in Admin → Webhooks.

# Example webhook body (only the fields you ticked are included; "event" is always sent)
POST https://your-site.com/payment/webhook
x-prime-pay-event: payment.verified
x-prime-pay-signature: <hex HMAC-SHA256 of the raw body>

{
  "event": "payment.verified",
  "order_code": "PP-123456789-1234",
  "external_order_id": "TG-5550001-1767000000",
  "amount": 588.82,
  "status": "verified",
  "utr": "FMPIB123456789",
  "metadata": { "chat_id": 5550001, "product_id": "d520" },
  "is_test": true          // only present for sandbox (pk_test_) orders
}
order_code, amount, status, utrSent by default.
metadata, external_order_id, device_info, base_amount, tax_amount, paid_atTick them in the website’s Webhook payload builder to receive them.
amountThe total the customer paid (includes tax when the site has a tax rate).

Before you go live

1Sandbox order created and your webhook handled it (HTTP 200).
2Signature is verified on the raw body, and each order is delivered only once.
3Keys are in server environment variables, not in browser code.
4A receiving UPI ID is set (Admin → Settings) and Gmail matching is linked (Admin → Overview checklist).
5Live key swapped in and one small real payment tested end-to-end.

Troubleshooting

401 on checkoutWrong or disabled key. Check the key and that the website is Active.
503 maintenanceMaintenance mode is on (Admin → Settings). New checkouts are paused.
429Per-website rate limit reached (default 60 requests/minute) — raise it in the website’s settings.
403 blockedThe caller’s IP is blacklisted — see Admin → Settings → Blocked IPs.
Webhook not arrivingURL must be public HTTPS and answer 2xx. Use Test webhook and check Admin → Webhooks for the response code.
No metadata in webhookTick “Metadata” in the website’s Webhook payload builder.
Customer paid but still pendingCheck Gmail linking and the Gmail logs; the customer can also enter the UTR on the checkout page.
Open admin dashboardNeed help? Contact us