Developer Integration Reference

Welcome to the official PayxMint API documentation. Use this reference to integrate high-fidelity UPI automated payments securely into your core applications.

Intent Status Codes
PENDINGCreated, awaiting UPI scan
SUCCESSPaid and reconciled by engine
EXPIREDTime window elapsed before payment
Integration Testing (Simulator)

Simulate successful payment state transitions during development by sending an incoming POST request to the simulation endpoint using your active order_id.

POST /api/v1/simulate-payment { "order_id": "your_order_id" }

API Environment, Auth & Idempotency

Authenticate all requests by including your secret API key in the Authorization header.

Production API Base Endpoint

https://payxmint.com/api/v1

Header AuthenticationAuthorization: Bearer YOUR_API_KEY
SAFETY FIRST

Idempotency Keys

Pass an Idempotency-Key: <UUID> header on POST /create-intent to prevent duplicate payments during retries.

Idempotency-Key: 9b1deb4d-3b7d-4bad...
THROTTLING

API Rate Limits

  • Create Intent: 1,000 TPS (60,000 req/min)
  • Check Status: 2,000 TPS (120,000 req/min)

Exceeded requests return HTTP 429 Too Many Requests with a Retry-After header.

Standard API Error Response

All error responses return standard HTTP status codes (400, 401, 404, 429, 500) with a structured error object:

JSON
{
  "error": {
    "type": "invalid_request_error",
    "code": "authentication_failed",
    "message": "Missing or invalid API key in Authorization header."
  }
}
Section 1

Collections & Inbound Payments

POST

1. Initiate Payment (/create-intent)

Creates a new dynamic payment intent and returns the checkout URL to showcase to your user, alongside high-fidelity raw UPI deep links.

Endpoint URL

https://payxmint.com/api/v1/create-intent

Request Body Payload

FieldTypeStatus
amountstring/numberRequired
order_idstringOptional (Alphanumeric, >= 8 chars)
customer_mobilestringOptional
customer_emailstringOptional
customer_ipstringOptional
customer_device_idstringOptional
redirect_urlstringOptional
gatewaystringOptional (PAYTM, GPAY, PHONEPE, PINELABS)
metadataobjectOptional

Interactive Request

Bash
curl --request POST \
  --url https://payxmint.com/api/v1/create-intent \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
    "amount": "5000.00",
    "order_id": "pay_202506038627",
    "customer_mobile": "9898989898",
    "customer_email": "amit_bansal@email.com",
    "redirect_url": "https://merchant.site/thankyou",
    "customer_ip": "192.168.1.1",
    "customer_device_id": "dev_xyz123",
    "gateway": "GPAY",
    "metadata": {
      "cart_id": "12345"
    }
  }'

Sample Response

JSON
{
  "id": "ptx_8f2a9c3d1e",
  "object": "payment_intent",
  "amount": 1850.00,
  "currency": "INR",
  "status": "PENDING",
  "order_id": "tx_873491023",
  "checkout_url": "https://payxmint.com/pay/ptx_8f2a9c3d1e",
  "payment_token": "ptx_8f2a9c3d1e",
  "upi_link": "upi://pay?pa=merchant@upi&pn=PayxMint...",
  "upi_links": {
    "upi": "upi://pay?pa=merchant@upi...",
    "gpay": "intent://...",
    "phonepe": "phonepe://...",
    "paytm": "paytmmp://..."
  },
  "qr_data": "upi://pay?pa=merchant@upi...",
  "metadata": {},
  "created": 1779187312
}
POST

2. Payment Status (/check-status)

Queries and retrieves the exact transaction metadata, settlement details, and collection status using your order_id.

Endpoint URL

https://payxmint.com/api/v1/check-status

Request Body Payload

FieldTypeStatus
order_idstringRequired (Unique Merchant ID)
💡 **Query Alternative**: You can also use HTTP GET to check status by appending order_id to search params:
https://payxmint.com/api/v1/check-status?order_id=pay_202506038627

Interactive Request

Bash
curl --request POST \
  --url https://payxmint.com/api/v1/check-status \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
    "order_id": "pay_202506038627"
  }'

Sample Response

JSON
{
  "id": "ptx_8f2a9c3d1e",
  "object": "payment_intent",
  "amount": 1850.00,
  "currency": "INR",
  "status": "SUCCESS",
  "order_id": "tx_873491023",
  "metadata": {},
  "payer": {
    "name": "John Doe",
    "upi": "johndoe@ybl"
  },
  "settlement": {
    "utr": "6120948375",
    "txn_id": "tx_612093847",
    "timestamp": "2026-05-19T17:11:45.000Z"
  },
  "created": 1779187312,
  "expire_at": 1779188212
}
SECURITY

3. Process Webhook Data (Payment)

To guarantee secure event delivery, calculate the HMAC-SHA256 hex signature directly from the raw incoming body string against your webhook secret.

⚠️ MANDATORY SAFETY NET: Prevent Double Crediting (Idempotency)

Payment systems operate under at-least-once delivery guarantees. Due to network retries, automated reconciliation reconcilers, or concurrent worker dispatches, your server may receive the same payment.success webhook multiple times.

1. Check Status Before CreditingQuery your database first. If the order_id is already marked as SUCCESS or CREDITED, do NOT credit the user again. Simply return HTTP 200.
2. Database Unique ConstraintEnforce a UNIQUE constraint on order_id / utr in your payments table to make duplicate credit inserts physically impossible.
3. Atomic Transactions / Mutex LockWrap your balance update inside an atomic database transaction (BEGIN ... COMMIT) or acquire a Redis lock on lock:order:{order_id} for 10 seconds.
4. Always Acknowledge with HTTP 200Always return an HTTP 200 OK response after processing (even on duplicate events) so PayxMint knows the event was delivered and halts retries.

Cryptographic Headers

X-PayxMint-Eventpayment.success
X-PayxMint-SignatureHex-encoded HMAC-SHA256

Verification Implementation (NodeJS)

Javascript
const crypto = require('crypto');

// 1. Webhook Secret from your PayxMint Dashboard
const webhookSecret = process.env.PAYXMINT_WEBHOOK_SECRET;

function handleIncomingWebhook(req, res) {
  const signature = req.headers['x-payxmint-signature'];
  const rawBody = req.rawBody; // Ensure raw unparsed string body

  // 2. Validate HMAC-SHA256 signature
  const expectedSignature = crypto
    .createHmac('sha256', webhookSecret)
    .update(rawBody)
    .digest('hex');

  if (signature !== expectedSignature) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  const payload = JSON.parse(rawBody);
  const { order_id, amount, utr } = payload;

  // 3. IDEMPOTENCY SAFETY NET: Check if order was already processed
  const order = await db.orders.findOne({ order_id });
  if (!order || order.status === 'SUCCESS') {
    // Already credited or processed! Acknowledge with 200 OK and EXIT safely.
    return res.status(200).json({ status: 'already_processed' });
  }

  // 4. Atomic Credit within Database Transaction
  await db.transaction(async (tx) => {
    await tx.orders.update({ where: { order_id }, data: { status: 'SUCCESS', utr } });
    await tx.wallets.increment({ where: { userId: order.userId }, data: { balance: amount } });
  });

  return res.status(200).json({ status: 'success' });
}

Sample Webhook Notification Payload

JSON
{
  "event": "payment.success",
  "status": "SUCCESS",
  "order_id": "pay_202506038627",
  "amount": 620.00,
  "utr": "7019283745",
  "payer_name": "John Doe",
  "payer_upi": "johndoe@ybl",
  "metadata": {},
  "timestamp": "2026-05-19T17:11:45.000Z"
}
HEADLESS

4. White-label / Custom Checkout Integration Flow

If you wish to host your own checkout page and retain full control over the user interface, you can use PayxMint as a headless payment engine. This avoids redirecting the user to our hosted checkout_url.

Step 1Backend Init

Server calls create-intent and extracts upi_links & qr_data.

Step 2UI Rendering

Render UPI app buttons for mobile or a high-res QR code on desktop.

Step 3Frontend Polling

Poll check-status every 3-4s to update UI and redirect.

Step 4Webhook Fulfill

Securely fulfill the order upon verified server-to-server webhook.

1

Initialize Payment Session from Backend

Call POST /api/v1/create-intent from your backend server. Extract the upi_links and qr_data strings and return them to your client application:

Node.js (Backend)
const response = await fetch("https://payxmint.com/api/v1/create-intent", {
  method: "POST",
  headers: {
    "Authorization": "Bearer YOUR_API_KEY",
    "Idempotency-Key": "order_" + Date.now(),
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    amount: 1000.50,
    order_id: "ORDER_" + Date.now(),
    redirect_url: "https://yourwebsite.com/payment/success"
  })
});

const intentData = await response.json();
// Pass intentData.upi_links and intentData.qr_data to your frontend
2A

Mobile Devices (Deep Links)

On mobile devices, render buttons that open installed UPI applications directly using the URLs provided in the upi_links object:

<!-- Dynamic Mobile UPI App Triggers -->
<a href={upi_links.phonepe} className="btn-phonepe">Pay with PhonePe</a>
<a href={upi_links.paytm} className="btn-paytm">Pay with Paytm</a>
<a href={upi_links.upi} className="btn-upi">Pay with Any UPI App</a>

💡 Note: UPI deep links are returned dynamically by the API. Never hardcode static UPI handles.

2B

Desktop Devices (QR Code)

On desktop, render a high-contrast QR code using qrcode.react from the raw qr_data string:

React / JSX
import React from 'react';
import { QRCodeSVG } from 'qrcode.react';

export default function DesktopQRCode({ qrData, amount }) {
  return (
    <div className="p-6 border border-slate-200 rounded-2xl text-center">
      <h3 className="text-sm font-bold text-slate-800 mb-2">Scan to Pay: ₹${amount}</h3>
      <div className="flex justify-center my-4">
        <QRCodeSVG value={qrData} size={220} includeMargin={true} />
      </div>
      <p className="text-xs text-slate-400 font-medium">
        Scan with Google Pay, PhonePe, Paytm, or BHIM
      </p>
    </div>
  );
}
3

Real-Time UI Status Updates (Polling)

While the user completes the payment on their phone, poll the check-status endpoint every 3 to 4 seconds to transition the UI instantly:

React / JSX
import React, { useEffect, useState } from 'react';

export default function CustomCheckoutPage({ orderId }) {
  const [status, setStatus] = useState("PENDING");

  useEffect(() => {
    if (status !== "PENDING") return; // Stop polling when completed

    const interval = setInterval(async () => {
      try {
        // In production, proxy through your backend or call check-status directly
        const response = await fetch(`https://payxmint.com/api/v1/check-status?order_id=${orderId}`, {
          headers: { "Authorization": "Bearer YOUR_API_KEY" }
        });
        const data = await response.json();

        if (data.status === "SUCCESS") {
          setStatus("SUCCESS");
          clearInterval(interval);
          // Redirect user or show success UI
          window.location.href = "/checkout/success?order_id=" + orderId;
        } else if (data.status === "EXPIRED") {
          setStatus("EXPIRED");
          clearInterval(interval);
        }
      } catch (err) {
        console.error("Polling error:", err);
      }
    }, 3500); // Check every 3.5 seconds

    return () => clearInterval(interval); // Cleanup on unmount
  }, [orderId, status]);

  if (status === "SUCCESS") return <div className="text-emerald-600 font-bold">Payment Successful! Redirecting...</div>;
  if (status === "EXPIRED") return <div className="text-rose-600 font-bold">Payment Expired. Please retry.</div>;

  return <div className="text-slate-600">Waiting for payment scan...</div>;
}
Step 4: Authoritative Fulfillment via WebhooksNever fulfill orders, credit balances, or release digital items based purely on frontend polling. Frontend requests can be intercepted or manipulated. Always wait for the signed, server-to-server payment.success Webhook before fulfilling orders in your database.
HIGH CONVERSION

5. Google Pay Native Intent Flow (Web & Mobile Apps)

Deliver a seamless, high-conversion payment flow for Google Pay users on Android devices using native intent image sharing. This opens the Google Pay payment interface directly from your web or mobile app with full device compatibility.

A. Web Custom Checkout (React / Next.js / Vue / Vanilla JS)

Convert the qr_data Data URI into a synchronous File on page load to preserve Transient User Activation, then trigger navigator.share() on button click:

Javascript / React
// 1. Helper: Convert base64 dataURI to File synchronously
function dataURItoFile(dataURI, filename) {
  const arr = dataURI.split(',');
  const mime = arr[0].match(/:(.*?);/)[1];
  const bstr = atob(arr[1]);
  let n = bstr.length;
  const u8arr = new Uint8Array(n);
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n);
  }
  return new File([u8arr], filename, { type: mime });
}

// 2. Click Handler: Trigger Native Share Sheet on Android
async function handleGooglePayClick(qrDataUrl) {
  try {
    const paymentFile = dataURItoFile(qrDataUrl, 'payxmint_payment_card.png');
    
    // Check if browser supports sharing image files
    if (navigator.canShare && navigator.canShare({ files: [paymentFile] })) {
      await navigator.share({
        files: [paymentFile],
        title: 'Pay with Google Pay'
      });
    } else {
      // Fallback for desktop browsers & restricted in-app WebViews (Instagram/Telegram)
      openOnScreenQrModal(qrDataUrl);
    }
  } catch (err) {
    if (err.name !== 'AbortError') {
      openOnScreenQrModal(qrDataUrl);
    }
  }
}
React Native

Using react-native-share

Javascript
import Share from 'react-native-share';

async function openGPay(qrBase64) {
  await Share.open({
    url: qrBase64,
    type: 'image/png',
    title: 'Pay with Google Pay',
    failOnCancel: false,
  });
}
Flutter

Using share_plus

Dart
import 'package:share_plus/share_plus.dart';

void shareToGPay(String filePath) {
  Share.shareXFiles(
    [XFile(filePath)],
    text: 'Pay with Google Pay',
  );
}
Android Native (Kotlin)

Using Intent.ACTION_SEND

Kotlin
val intent = Intent(Intent.ACTION_SEND).apply {
    type = "image/png"
    putExtra(Intent.EXTRA_STREAM, qrFileUri)
    addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
startActivity(Intent.createChooser(intent, "Pay with Google Pay"))

💡 Card Image Aspect Ratio Best Practice (Universal 5:4 Standard)

Different Android phone models format their share sheet thumbnails differently (Samsung uses a 1:1 box; Xiaomi & Vivo use a 4:3 card; Pixel uses a 16:9 banner). Rendering your payment voucher card in a 5:4 aspect ratio (1000 × 800px) with an 85% central safe zone guarantees that your brand, amount, and QR code stay large, crisp, and never get cropped across any phone.

GET

6. Webhook Event Logs (/events)

Query past webhook delivery attempts, inspect payloads, and audit failed or retried notifications for reconciliation.

Endpoint URL & Query Params

https://payxmint.com/api/v1/events?limit=10&offset=0

Interactive Request

Bash
curl --request GET \
  --url 'https://payxmint.com/api/v1/events?limit=10&offset=0' \
  --header 'Authorization: Bearer YOUR_API_KEY'

Sample Response

JSON
{
  "object": "list",
  "url": "/v1/events",
  "has_more": false,
  "data": [
    {
      "id": "evt_8f1a2b3c4d",
      "object": "event",
      "type": "payment.success",
      "created": 1779187320,
      "data": {
        "event": "payment.success",
        "order_id": "pay_202506038627",
        "amount": 5000.00,
        "utr": "7019283745"
      },
      "status": "DELIVERED",
      "retry_count": 0,
      "next_retry": null
    }
  ]
}
NO-CODE

7. eCommerce Plugins

We provide ready-to-use plugins for major eCommerce platforms. Integrate PayxMinT in minutes without writing a single line of code.

WooCommerce / WordPress

Full Gateway Integration

  1. Download the official WooCommerce plugin
  2. Upload via Plugins → Add New in WordPress
  3. Activate PayxMint Payment Gateway
  4. Enter your Merchant ID and API Keys in Settings
Download Plugin (.zip)

Shopify AppComing Soon

Offsite Payment Gateway

Due to strict Shopify Partner regulations, a direct zip installation is not possible. We are currently getting our official App approved on the Shopify App Store.