Merchant Documentation

Overview

MyPayIndia Merchants API lets you accept payments on your website or app. You create a checkout session on your server, redirect your customer to the MyPayIndia checkout page, and we redirect them back to your site once the payment is complete. You then verify the payment server-side.


To get started, you need a merchant key. To get one, you can create it yourself at https://mypayindia.com/merchant/new for a small, one-time fee.

Getting Started

You'll have two keys:

Publishable key (pk_live_...) identifies your merchant account. It's safe to include anywhere, you can even post it to the public on your twitter account if you want. I wouldn't do that though.

Secret key (sk_live_...) is used to authenticate yourself in API requests. This one's private. Don't give it to anyone, don't expose it anywhere publicly.


Your secret key is shown once when you generate your API keys. If you lose it or have it leaked (damn bruh), you can regenerate it from the Merchant Panel but any existing integrations using the old key will break.

Checkout Flow

  1. Your server calls the Create Session API with the amount, order ID, and a return URL. You authenticate with your secret key.
  2. You redirect your customer to the checkout_url returned by the API.
  3. The customer logs into MyPayIndia (if not already), reviews the payment details, and clicks Pay.
  4. MyPayIndia redirects the customer back to your return_url with the ?session_id=cs_xxx&status=complete parameters appended.
  5. Your server calls the Status API with the session ID to verify the payment actually completed. Do not trust the URL parameters alone.
    An alternative to this is to use webhooks to get notified of transaction status changes without having to call the Status API.

Create a Checkout Session

POST https://mypayindia.com/api/v2/pay/create

Request Headers

Header Value
Authorization Bearer sk_live_your_secret_key
Content-Type application/json

Request Body

Parameter Type Description
amount integer required Amount. How much you want the customer to pay. This value expects paisa (10000 = 100.00 INR). Must be greater than 0 and lesser than 100000000 (1m INR)
return_url string required URL to redirect the customer to after payment. Must be one of your allowed domains.
order_id string optional Your order reference. A max of 64 characters are allowed.

Example Request

curl -X POST https://mypayindia.com/api/v2/pay/create \
  -H "Authorization: Bearer sk_live_your_secret_key" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 25000,
    "order_id": "yuri-1930",
    "return_url": "https://mycoolandawesomewebsite.com/i-get-paid/i-got-paid"
  }'

Example Response

{
  "success": true,
  "data": {
    "session_id": "cs_a1b2c3d4e5f6...",
    "checkout_url": "https://mypayindia.com/pay?session=cs_a1b2c3d4e5f6...",
    "expires_at": "2026-04-18T15:30:00+00:00"
  }
}

Errors

Code HTTP Description
4001 403 return_url domain not in allowed domains
4002 403 Amount exceeds key's limit (unreviewed/unverified)
9004 400 Invalid or missing input
Redirect your customer to checkout_url. Checkout sessions expire after 30 minutes.

Verify Payment

After the customer is redirected back to your return_url, verify the payment server-side. Do NOT trust the URL parameters alone. You can verify the payment went through by calling the Status API documented below. If you don't wanna do that, you can use webhooks instead. Or you can combine both.

GET https://mypayindia.com/api/v2/pay/status?session_id=cs_xxx

Request Headers

Header Value
Authorization Bearer sk_live_your_secret_key

Example Response (completed)

{
  "success": true,
  "data": {
    "session_id": "cs_a1b2c3d4e5f6...",
    "status": "complete",
    "amount": 25000,
    "order_id": "yuri-1930",
    "transaction_id": "TXN-1234567890-12ab",
    "transaction_status": "confirmed",
    "sender_username": "johnpayment",
    "sender_id": 69,
    "created_at": "2026-04-18T15:00:00+00:00",
    "expires_at": "2026-04-18T15:30:00+00:00",
    "completed_at": "2026-04-18T15:01:37+00:00"
  }
}

Session Statuses

Status Meaning
open Waiting for the customer to pay. Checkout session is still valid.
complete Customer went through checkout and a transaction was CREATED (emphasized because it may be pending or any other state, you may not have gotten your funds yet). Checkout session is closed by this point.
expired Checkout session expired before the customer paid in the 30 minute window.

Transaction Statuses

When session status is complete, the transaction_status field tells you what happened with the actual money:

Status Meaning
confirmed Payment completed successfully. Funds are now in your account.
pending Payment is under manual review (large transactions). Funds are not in your account yet.
rejected Payment was rejected during review. Funds were returned to the sender.
reversed Payment was reversed by MyPayIndia staff after being confirmed. Funds have been returned to the sender.

Errors

Code HTTP Description
9005 404 Session not found
9004 400 Invalid or missing input

Webhooks

Webhooks notify your server when a transaction status changes. This is useful for cases where the customer closes their browser before being redirected back to your site, or when a transaction status changes after checkout (for example a pending transaction gets confirmed or reversed).

You can set up webhook endpoints in the Merchant Panel in your merchant key's settings page. You can subscribe to specific events and configure multiple endpoints.

How it works

When an event occurs, MyPayIndia sends a POST request to your endpoint URL with a JSON payload. Your endpoint should return a 2xx status code to acknowledge that it's received it. If your endpoint is down or returns an error, we retry with exponential backoff (1 min, 2 min, 4 min, up to ~4 hours) before giving up.

Webhook Headers

Header Description
X-MPI-Event The event type, e.g. transaction.confirmed
X-MPI-Signature HMAC-SHA256 signature of the request body, signed with your endpoint's signing secret
X-MPI-Delivery Unique delivery ID. Use this to deduplicate if you receive the same event twice.

Example Payload

{
  "delivery_id": "whd_a1b2c3d4e5f6...",
  "event": "transaction.confirmed",
  "created_at": "2026-04-18T15:01:37+00:00",
  "owner_type": "merchant_key",
  "owner_id": 1,
  "data": {
    "transaction_id": "TXN-1234567890-12ab",
    "session_id": "cs_a1b2c3d4e5f6...",
    "amount": 250.00,
    "order_id": "yuri-1930",
    "status": "confirmed",
    "sender_username": "johnpayment",
    "sender_id": 69
  }
}

Webhook Events

Event Fired when
transaction.confirmed Payment completed, confirmed and funds are now in your account.
transaction.pending Payment created but held for manual review (large transactions). Funds are not yet in your account.
transaction.reverted A previously confirmed or pending transaction was reversed or rejected. Funds have been returned to the sender.
test.ping Sent when you click "Test" in the Merchant Panel. Use this to verify your endpoint is reachable. This event is always delivered regardless of your event subscriptions.
subscription.created A customer subscribed to one of your plans.
subscription.renewed Subscription renewed and payment was collected for the new billing period.
subscription.cancelled Subscription was cancelled. Check data.reason: user_cancelled (customer requested), period_ended (cancellation took effect), merchant_immediate or merchant_end_of_period (merchant cancelled).
subscription.past_due Payment failed on a subscription. Retrying automatically.
subscription.expired Subscription expired after all payment retries were exhausted.
subscription.resumed Customer undid a pending cancellation.
invoice.paid Subscription invoice was paid successfully.
invoice.payment_failed Subscription invoice payment attempt failed. Will retry automatically.

Signature Verification

Every webhook request includes an X-MPI-Signature header containing an HMAC-SHA256 hash of the request body, signed with your endpoint's signing secret. Always verify this signature before processing the webhook to ensure it came from MyPayIndia.

Node.js

const crypto = require('crypto');

app.post('/webhooks/mypayindia', (req, res) => {
  const signature = req.headers['x-mpi-signature'];
  const secret = 'your_webhook_signing_secret';

  const expected = crypto
    .createHmac('sha256', secret)
    .update(req.rawBody)
    .digest('hex');

  if (signature !== expected) {
    return res.status(401).send('Invalid signature');
  }

  const event = req.body;
  // Process the event
  res.status(200).send('OK');
});

Python

import hmac
import hashlib

def handle_webhook(request):
    raw_body = request.get_data()
    signature = request.headers.get('X-MPI-Signature', '')
    secret = b'your_webhook_signing_secret'

    expected = hmac.new(secret, raw_body, hashlib.sha256).hexdigest()

    if not hmac.compare_digest(expected, signature):
        return 'Invalid signature', 401

    event = request.get_json()
    # Process the event
    return 'OK', 200

PHP

$raw_body = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_MPI_SIGNATURE'] ?? '';
$secret = 'your_webhook_signing_secret';

$expected = hash_hmac('sha256', $raw_body, $secret);

if (!hash_equals($expected, $signature)) {
    http_response_code(401);
    exit('Invalid signature');
}

$event = json_decode($raw_body, true);
// Process the event
http_response_code(200);

Important: Compute the HMAC against the raw request body, not a re-serialized version. Re-encoding JSON may change formatting and produce a different hash.

Subscriptions

MyPayIndia supports recurring billing through subscription plans. You create plans with a fixed amount and billing interval, then use the same checkout flow to subscribe customers. MyPayIndia handles billing automatically, charging the customer's balance on each cycle and notifying you via webhooks.


Plans are created and managed from the Merchant Panel in your merchant key's settings page. The API provides read access to plans and full management of subscriptions and invoices.

Plans

A plan defines the pricing and billing interval for a subscription. Plans are created in the Merchant Panel.


Plans have a fixed amount, a billing interval (daily, weekly, monthly, or yearly), an interval count (e.g. every 3 months), and an optional trial period in days. Once created, the amount and interval cannot be changed - create a new plan instead.


Deactivating a plan prevents new subscriptions but does not affect existing subscribers. Plans with active subscriptions cannot be deleted.

Retrieve Plans

GET https://mypayindia.com/api/v2/pay/plans
Parameter Type Description
id integer optional Get a specific plan by ID
include_inactive string optional Set to true to include deactivated plans

Example Response

{
  "success": true,
  "data": [
    {
      "id": 6,
      "name": "MyPayIndia Premium",
      "amount": 25000,
      "interval": "monthly",
      "interval_count": 1,
      "trial_period_days": 7,
      "active": true,
      "created_at": "2026-06-04T10:37:55+00:00"
    }
  ]
}
Amounts are in paisa. 25000 = 250.00 INR.

Subscription Checkout

To subscribe a customer, create a checkout session with a plan_id instead of an amount. The rest of the flow is identical to one time payments.

POST https://mypayindia.com/api/v2/pay/create

Request Body

Parameter Type Description
plan_id integer required The plan to subscribe the customer to. Cannot be combined with amount.
return_url string required URL to redirect the customer to after checkout.
order_id string optional Your order reference (max 64 characters).

Example Request

curl -X POST https://mypayindia.com/api/v2/pay/create \
  -H "Authorization: Bearer sk_live_your_secret_key" \
  -H "Content-Type: application/json" \
  -d '{
    "plan_id": 6,
    "return_url": "https://mycoolandawesomewebsite.com/i-get-paid/i-get-paid-consistently",
    "order_id": "yuri-1931"
  }'

Example Response

{
  "success": true,
  "data": {
    "session_id": "ps_a1b2c3d4e5f6...",
    "checkout_url": "https://mypayindia.com/pay?session=ps_a1b2c3d4e5f6...",
    "expires_at": "2026-06-04T15:30:00+00:00",
    "plan_id": 6,
    "mode": "subscription"
  }
}

What happens at checkout

  • Plan without trial: Customer is charged immediately. Subscription starts as active.
  • Plan with trial: No charge at checkout. Subscription starts as trialing. First charge happens when the trial ends.
  • If the customer already has an active subscription to this plan, checkout is blocked.
The session status endpoint (GET /api/v2/pay/status) includes mode, plan_id, subscription_id, and subscription_status for subscription sessions.

Manage Subscriptions

List Subscriptions

GET https://mypayindia.com/api/v2/pay/subscriptions
Parameter Type Description
id string optional Get a specific subscription by its sub_xxx ID
status string optional Filter by status: active, trialing, past_due, cancelled, expired
plan_id integer optional Filter by plan

Example Response

{
  "success": true,
  "data": [
    {
      "subscription_id": "sub_a1b2c3d4e5f6...",
      "plan_id": 6,
      "plan_name": "MyPayIndia Premium",
      "status": "active",
      "payer_username": "johnpayment",
      "payer_id": 123,
      "amount": 25000,
      "interval": "monthly",
      "interval_count": 1,
      "cancel_at_period_end": false,
      "current_period_start": "2026-06-04T10:00:00+00:00",
      "current_period_end": "2026-07-04T10:00:00+00:00",
      "cancelled_at": null,
      "created_at": "2026-06-04T10:00:00+00:00"
    }
  ]
}

Cancel a Subscription

DELETE https://mypayindia.com/api/v2/pay/subscriptions?id=sub_xxx

By default, the subscription remains active until the end of the current billing period. Pass {"immediate": true} in the request body to cancel immediately.

Example Response

{
  "success": true,
  "data": {
    "subscription_id": "sub_a1b2c3d4e5f6...",
    "status": "active",
    "cancel_at_period_end": true
  }
}
Customers can also cancel their own subscriptions from their account dashboard. When they do, it always cancels at period end (never immediately). They can also undo a pending cancellation before the period ends.

Invoices

Every billing cycle creates an invoice. Invoices track the payment status for each period.

GET https://mypayindia.com/api/v2/pay/invoices?subscription_id=sub_xxx

Example Response

{
  "success": true,
  "data": [
    {
      "invoice_id": "inv_a1b2c3d4e5f6...",
      "amount": 25000,
      "status": "paid",
      "transaction_id": "TXN-1234567890-12ab",
      "attempts": 1,
      "period_start": "2026-06-04T10:00:00+00:00",
      "period_end": "2026-07-04T10:00:00+00:00",
      "paid_at": "2026-06-04T10:00:00+00:00",
      "created_at": "2026-06-04T10:00:00+00:00"
    }
  ]
}

Invoice Statuses

Status Meaning
paid Payment successful. Funds are in your account.
pending Payment failed but retrying. Check attempts for retry count.
failed All retry attempts exhausted. Payment was not collected.
void Invoice was voided (subscription expired before payment could be collected).

Subscription Lifecycle

Statuses

Status Meaning
trialing In free trial period. No charges until trial ends.
active Subscription is active and billing normally.
past_due Payment failed. MyPayIndia is retrying with exponential backoff (1h, 2h, 4h, 8h). The subscription remains in this state during retries.
cancelled Subscription was cancelled by the customer or merchant.
expired All payment retries exhausted. Subscription is terminated.

Billing Behavior

MyPayIndia automatically charges the customer's account balance at the start of each billing period. If the charge fails (insufficient balance), the invoice enters a retry cycle with exponential backoff. After 5 failed attempts (~15 hours), the invoice is marked as failed and the subscription is expired.

Cancellation

When a subscription is cancelled at period end (cancel_at_period_end: true), it remains active and functional until the current period ends. No further charges are made. The customer can undo a pending cancellation before the period ends.

Immediate cancellation terminates the subscription right away. This is only available via the merchant API or Merchant Panel, not to customers directly.

API Reference

All API requests are authenticated via the Authorization: Bearer sk_live_... header. All request and response bodies are JSON. Timestamps are in ISO 8601 format.

Checkout

POST /api/v2/pay/create

Create a checkout session. Pass amount for one-time payments or plan_id for subscriptions. See Create Session and Subscription Checkout for full details.

GET /api/v2/pay/status?session_id=cs_xxx

Get the status of a checkout session. See Verify Payment above for full details.

Webhook Endpoints

POST /api/v2/webhooks/endpoints

Create a webhook endpoint. Body: url (string), events (array of event strings), description (optional string). Returns the endpoint with a signing secret shown once.

GET /api/v2/webhooks/endpoints

List all your webhook endpoints.

GET /api/v2/webhooks/endpoints?id=5

Get a single webhook endpoint by ID.

PATCH /api/v2/webhooks/endpoints?id=5

Update a webhook endpoint. Body can include: url, events, description, active (boolean).

DELETE /api/v2/webhooks/endpoints?id=5

Delete a webhook endpoint. Also deletes all delivery history for this endpoint.

POST /api/v2/webhooks/rotate_secret?id=5

Rotate the signing secret for an endpoint. Returns the new secret once. The old secret stops working immediately.

Webhook Deliveries

GET /api/v2/webhooks/deliveries?endpoint_id=5

List recent deliveries for a webhook endpoint. Optional: limit (default 50, max 200).

POST /api/v2/webhooks/deliveries?redeliver=whd_xxx

Manually redeliver a specific webhook delivery. Resets the retry counter.

Plans

GET /api/v2/pay/plans

List all active plans. Pass ?id=6 for a specific plan, or ?include_inactive=true to include deactivated plans.

Subscriptions

GET /api/v2/pay/subscriptions

List all subscriptions. Optional filters: ?status=active, ?plan_id=6. Pass ?id=sub_xxx for a specific subscription.

DELETE /api/v2/pay/subscriptions?id=sub_xxx

Cancel a subscription. By default cancels at end of billing period. Pass {"immediate": true} in the request body to cancel immediately. The subscriber is notified by email.

Invoices

GET /api/v2/pay/invoices?subscription_id=sub_xxx

List all invoices for a subscription. Returns payment status, transaction ID, retry attempts, and billing period for each invoice.