Payment Integration
This guide covers integrating the MP Merchant checkout flow into web apps, mobile apps, and chat bots — including the hosted payment page, real-time SSE updates, and idempotent order creation.
How it works
The payment flow has four stages:
Creating an order
Use POST /v1/orders to create a payment order. All amounts are in USDT.
amountrequiredstring | Payment amount in USDT. Use a decimal string, e.g. "99.99". |
currencyrequiredstring | Must be "USDT". |
networkrequiredstring | Blockchain network: "TRC20" (TRON) or "ERC20" (Ethereum). TRC20 is recommended for lower fees. |
descriptionstring | Human-readable description shown on the checkout page. |
return_urlstring | URL to redirect the customer after payment. See security note below. |
webhook_urlstring | URL to receive payment event notifications. Overrides the default webhook URL for this order. |
metadataobject | Arbitrary key-value pairs stored with the order. Returned in webhook events. |
idempotency_keystring | Unique key to safely retry order creation without creating duplicates. See Idempotency. |
tolerance_percentnumber· default: 0.01 | Acceptable underpayment tolerance as a decimal (0.01 = 1%). Orders within tolerance are marked PAID. |
Handling the payment URL
Every order response includes a payment_url. This is the hosted checkout page where customers complete their payment. Choose the integration approach that fits your platform:
// Full redirect (recommended)
window.location.href = order.payment_url;
// Or open in a new tab
window.open(order.payment_url, '_blank');
// Or embed in an iframe
<iframe
src={order.payment_url}
width="100%"
height="600"
frameBorder="0"
/>💡 MPChat deep links
mpchat://pay?order={order_id} deep link to open the payment natively. Detect the user agent with navigator.userAgent.includes('MPChat').Real-time updates (SSE)
Instead of polling, subscribe to order events via Server-Sent Events (SSE). The connection closes automatically when the order reaches a terminal state.
const es = new EventSource(
`/v1/orders/${orderId}/events`,
{ headers: { Authorization: `Bearer ${keyId}:${ts}:${sig}` } }
);
es.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('Status update:', data.status);
if (['PAID', 'EXPIRED', 'UNDERPAID', 'FROZEN'].includes(data.status)) {
es.close(); // Terminal state reached
}
};
es.onerror = () => es.close();return_url handling
After payment (or expiry), the customer is redirected to your return_url. The order ID is appended as a query parameter.
https://acme.com/payment/return?order_id=ord_01HQ...🚨 Security: Never trust the redirect
return_url is not a payment confirmation. A malicious user could craft a redirect URL manually. Always verify payment status server-side by calling GET /v1/orders/{id} or by relying on webhooks.Idempotency
To safely retry order creation (e.g., after a network timeout), pass anidempotency_key. If you retry with the same key, the original order is returned without creating a duplicate.
curl -X POST https://api.mpchat.com/v1/orders \
-H "Authorization: Bearer ..." \
-H "Content-Type: application/json" \
-d '{
"amount": "99.99",
"currency": "USDT",
"network": "TRC20",
"idempotency_key": "checkout_session_a1b2c3"
}'Idempotency keys are scoped to your merchant account and expire after 24 hours. Use a value unique to your checkout session (e.g., your internal cart or session ID).
Error handling with retries
import time
import requests
def create_order_with_retry(payload, max_retries=3):
for attempt in range(max_retries):
try:
response = requests.post(
"https://api.mpchat.com/v1/orders",
json=payload,
headers={"Authorization": get_auth_header()},
timeout=10,
)
response.raise_for_status()
return response.json()
except requests.exceptions.Timeout:
if attempt == max_retries - 1:
raise
time.sleep(2 ** attempt) # 1s, 2s, 4s
except requests.exceptions.HTTPError as e:
if e.response.status_code < 500:
raise # Client errors don't retry
if attempt == max_retries - 1:
raise
time.sleep(2 ** attempt)Next steps
- Webhooks — receive payment events server-side
- API Reference — full endpoint documentation
- Error Reference — troubleshoot integration errors