My Pay
Step-by-step setup

Integrate in minutes — works with any platform

Complete file-by-file guide showing exactly where to put code, how to wire up the "Pay Now" button, and how to poll for payment updates — no webhook setup needed. Pick your stack below and follow along.

New to API integration? Read this first
You only need one thing from us — a Secret API key (looks like sk_live_xxx…). That single key does everything: create payments, check status, refund, list transactions, see balance.

No webhook setup. No HTTPS endpoint. No signing secret. You don't need to expose any URL to the internet — even shared hosting (cPanel) works. Status updates come back to you via a simple cron job that reads our events feed.

How payments flow

1. Create session
Your server calls POST /v1/sessions with the order amount and gets back a checkout_url.
2. Redirect customer
Send the customer to that URL. They pay via crypto / mobile banking on our hosted page.
3. Poll for updates
A small cron on your server hits GET /v1/events every minute. When it sees session.approved, mark the order paid.
Which URL is which? (read this first)
In every code example below you'll see two kinds of URLs. Don't mix them up.
1. Gateway URL (ours — My Pay)
[api_url not set]/v1/sessions[api_url not set]/v1/events

These are our servers. You don't change them. Same URL for every merchant.

2. Your website URLs (replace yoursite.com)

Anywhere you see https://yoursite.com/..., replace it with your own domain. These are pages on your site we redirect the customer back to:

  • success_url→ after successful payment
  • cancel_url→ if customer clicks "Cancel"
  • failed_url→ if the payment fails

⚠️ Don't trust success_url alone — the customer can manipulate the URL. Always confirm via the events feed (polling) or by calling GET /v1/sessions/{id}.

How the whole system works — in detail

One API key runs everything. Below is a step-by-step breakdown — what files to put on your website, what each file does, and how the API talks to your server.

At a glance — the payment journey
  Customer                Your Website                 My Pay ([api_url not set])
     │                         │                              │
     │ ── Click "Pay Now" ───▶ │                              │
     │                         │ ── POST /v1/sessions ──────▶ │   (Bearer sk_live_…)
     │                         │ ◀──── { checkout_url } ───── │
     │ ◀── 302 redirect ────── │                              │
     │                                                        │
     │ ────── Pays (crypto / mobile banking) ───────────────▶ │
     │                                                        │
     │ ◀── redirect → success_url / failed_url / cancel_url ──│
     │                         │                              │
     │                         │ Cron: GET /v1/events ──────▶ │   (every 60 sec)
     │                         │ ◀── [ session.approved ] ─── │
     │                         │                              │
     │                         │ Update order in your DB      │
     │                         │ (paid / failed)              │
What to upload to your website (5 pieces in total)
Same structure regardless of language (PHP, Node, Python, Laravel, WordPress…).
1
config (.env / config.php)

Just one variable —

text
MYPAY_SECRET=sk_live_xxxxxxxxxxxxxxxx

⚠️ Never put this in frontend code. Server-side files only.

2
pay.php / /api/pay / PaymentController

Runs when the customer clicks "Pay Now". Takes the amount + order_id, calls our API, and redirects the customer to checkout_url.

text
POST [api_url not set]/v1/sessions
Authorization: Bearer $MYPAY_SECRET
{ "amount": 1500, "currency": "BDT",
  "reference_id": "ORDER-12345",
  "success_url": "https://yoursite.com/success",
  "cancel_url":  "https://yoursite.com/cancel",
  "failed_url":  "https://yoursite.com/failed",
  "metadata": { "order_id": 12345 } }

⚠️ Take checkout_url from the response and redirect the customer there.

3
success.php / cancel.php / failed.php (3 pages)

Where the customer lands after payment. Just simple "Thank you" / "Try again" pages.

⚠️ Do NOT update the DB here — customers can manipulate the URL. The real confirmation comes from the cron.

4
poll-mypay.php / poll-mypay.js (cron script)

Runs every 60 seconds. Fetches new events since the last event id and updates your DB.

text
GET [api_url not set]/v1/events?since=<last_event_id>
Authorization: Bearer $MYPAY_SECRET

# response:
{ "events": [
    { "id": 892, "type": "session.approved",
      "session_id": "ses_…", "reference_id": "ORDER-12345",
      "amount": 1500, "status": "paid" }
  ],
  "last_id": 892 }

⚠️ Save last_id in DB or a file — pass it as since= on the next call.

5
cron job entry (cPanel / VPS / Vercel Cron)

In your hosting panel, schedule a cron that runs the poll script every minute.

text
# cPanel / crontab example
* * * * * /usr/bin/php /home/user/public_html/poll-mypay.php >/dev/null 2>&1

⚠️ On Vercel use the crons block in vercel.json; on Cloudflare use wrangler cron — same idea.

What the API actually does — endpoint by endpoint
EndpointWhen you use itWhat it returns
POST /v1/sessionsWhen an order is placed — to create a checkout pagecheckout_url + session_id
GET /v1/sessions/{id}To check the live status of a single order at any timestatus: pending / paid / failed / expired
GET /v1/events?since=From your cron — to fetch every new update in one callevents[] + last_id
GET /v1/transactionsTo build a dashboard or reconciliation reporttransaction list (paged)
POST /v1/refundsTo issue a refund from your admin panelrefund_id + status
GET /v1/balanceTo display available balance{ available, pending, currency }

These 6 endpoints together cover the entire payment lifecycle — create, status, events, list, refund, balance. Every call uses the same header: Authorization: Bearer sk_live_…

Why polling instead of webhooks?

Webhooks mean we push HTTP requests to your server. That requires a public HTTPS URL, signing-secret verification, and retry handling — painful on shared hosting, cPanel, or localhost.

Polling means you come to us and pull events. No public URL needed, no signature checks — just the same API key. A single cron job is enough. It works on any host, even ones that block inbound webhooks.

  • ✅ No signing secret to manage
  • ✅ No HTTPS or public endpoint required
  • ✅ No missed events — since=<last_id> replays everything
  • ✅ No duplicate-delivery problems — same data every time you read
Minimum requirement — just these 3 things
  1. One API secret key (generated from the dashboard)
  2. One server-side "create-payment" route that calls /v1/sessions
  3. One cron job that pulls updates from /v1/events

That's it. No SDK to install, no library required, no webhook URL to set up. Copy/paste the ready-made code for your platform below.

Before you start

1
Sign up & add your website
Create an account → go to Websites → add your domain (e.g. https://myshop.com). For Live keys you must verify domain ownership; Test keys work without it.
2
Generate your API Secret Key
Go to Dashboard → API KeysGenerate. Copy the sk_live_… (or sk_test_… for sandbox). Paste it into your .env / config.php as MYPAY_SECRET.⚠️ The secret is shown only ONCE. Never paste it into browser/frontend code.
3
That's it — no webhook, no signing secret
My Pay does NOT use webhooks. Instead, your server runs a small cron job every minute that calls GET /v1/events?since=<last_id> using the same secret key. You'll save the last last_event_id somewhere (DB, Redis, a file). The polling script below handles this — copy/paste.

The 3 pieces in your app

Whatever language/framework you use, you'll create these three things:

1. Create-payment route
e.g. POST /pay. Calls our API, redirects customer to checkout_url.
2. Polling cron job
e.g. poll-mypay.php running every 60s via cron. Calls /v1/events and marks orders paid.
3. Result pages
Three URLs: /success, /cancel, /failed. Just simple thank-you / try-again pages.

Setup for your platform

Selected: PHP (cPanel)
1
Create folder structure
In your project root create config.php, pay.php, poll-mypay.php, success.php, cancel.php, failed.php.
2
Add Pay Now button
A simple HTML form pointing to pay.php.
3
Schedule the poller every minute
In cPanel → Cron Jobs (or crontab -e) add: * * * * * /usr/bin/php /path/to/poll-mypay.php

Files to create

config.phpAPI key — copy from Dashboard → API Keys
php
<?php
// 👉 Get this from Dashboard → API Keys → Generate → copy "sk_live_..."
define('GATEWAY_URL',    '[api_url not set]');
define('MYPAY_SECRET',   'sk_live_xxxxxxxxxxxxxxxx');
// Use sk_test_... during development (sandbox mode)
checkout.htmlPay Now button
html
<form method="POST" action="/pay.php">
  <input type="hidden" name="order_id" value="12345">
  <input type="hidden" name="amount" value="1500">
  <button type="submit">Pay Now</button>
</form>
pay.phpCreates payment session and redirects
php
<?php
require 'config.php';
$orderId = $_POST['order_id'];
$amount  = $_POST['amount'];

$ch = curl_init(GATEWAY_URL . '/v1/sessions');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_POST           => true,
  CURLOPT_HTTPHEADER     => [
    'Authorization: Bearer ' . MYPAY_SECRET,
    'Content-Type: application/json',
  ],
  CURLOPT_POSTFIELDS => json_encode([
    'amount'       => (float) $amount,
    'currency'     => 'BDT',
    'reference_id' => 'ORDER-' . $orderId,
    'success_url'  => 'https://yoursite.com/success.php',
    'cancel_url'   => 'https://yoursite.com/cancel.php',
    'failed_url'   => 'https://yoursite.com/failed.php',
    'metadata'     => ['order_id' => $orderId],
  ]),
]);
$res = json_decode(curl_exec($ch), true);
header('Location: ' . $res['checkout_url']);
exit;
poll-mypay.phpCron job — runs every minute to fetch payment events
php
<?php
require 'config.php';
$lastId = (int) (@file_get_contents(__DIR__.'/last_event_id.txt') ?: 0);

$ch = curl_init(GATEWAY_URL . "/v1/events?since=$lastId&limit=100");
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . MYPAY_SECRET],
]);
$res = json_decode(curl_exec($ch), true);

foreach ($res['data'] ?? [] as $ev) {
  if ($ev['event_type'] === 'session.approved') {
    $orderId = $ev['payload']['reference_id'] ?? null;
    // 👉 UPDATE orders SET status='paid' WHERE id='$orderId'
  }
}
file_put_contents(__DIR__.'/last_event_id.txt', (string)($res['last_event_id'] ?? $lastId));
Testing checklist
Use sk_test_… key during development (sandbox mode)
'Pay Now' button redirects to gateway page
Successful payment returns to /success
Cancel returns to /cancel
Polling cron runs every minute and updates DB
Order marked paid only via polling, NOT via /success URL
Test polling locally — works even without HTTPS / public URL
Going live
Swap sk_test_… for sk_live_… in your .env
Verify your domain on the dashboard (required for Live keys)
Make sure the polling cron is registered on your host (cPanel / VPS / Vercel Cron)
Persist last_event_id in DB or a file — never lose it
Set env var on your host (Vercel / cPanel / VPS)
Run a small real payment (৳10) end-to-end
Never commit secret key to Git or expose to frontend

Troubleshooting

ProblemCauseFix
401 invalid_api_keyWrong/revoked keyGenerate a new key in /app/api-keys
No redirect happenscheckout_url empty in responseLog the JSON response from /v1/sessions
Order stays unpaidPolling cron not runningCheck crontab / cPanel cron / Vercel cron logs
Same event processed twicelast_event_id not persistedSave it in DB transactionally with the order update
Success page reached but unpaidUser manipulated URLTrust the events feed, not success_url
Test keys can't see live dataSandbox isolation (by design)Use sk_live_… for production data

Ready to start?

Generate your test API key and try it in 2 minutes.