This guide walks through a complete payment integration: generating a deposit address, monitoring for payment, and confirming receipt.
Architecture Overview
Customer visits checkout
↓
Backend: POST /pos/sessions ← create session with amount
↓
Return: depositAddress + qrCode
↓
Display QR code to customer
↓
Customer sends crypto
↓
Myaza detects on-chain tx
↓
Myaza POSTs to your webhookUrl
↓
Your server marks order as paid
Step 1: Create a Payment Session
When a customer checks out, create a POS session from your backend:
// POST /api/v1/pos/sessions
const session = await fetch(
"https://secureapi.gridlog.io/api/v1/pos/sessions",
{
method: "POST",
headers: {
"X-API-Key": process.env.MYAZA_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
amount: "49.99", // fiat amount
currency: "USD",
chain: "polygon",
token: "USDC",
expiresInMinutes: 15,
metadata: {
orderId: order.id,
userId: user.id,
},
}),
},
).then((r) => r.json());
// Store session in your database
await db.orders.update(order.id, {
paymentSessionId: session.sessionId,
depositAddress: session.depositAddress,
expiresAt: session.expiresAt,
});
Step 2: Show the Payment UI
Return the QR code and deposit address to your frontend:
// Frontend: display to customer
<img src={session.qrCode} alt="Scan to pay" />
<p>Or send {session.cryptoAmount} USDC to:</p>
<code>{session.depositAddress}</code>
<p>Session expires at {new Date(session.expiresAt).toLocaleTimeString()}</p>
Step 3: Handle the Webhook
Configure your webhook endpoint URL in the Myaza Dashboard (Settings → Webhooks → Endpoint URL). Myaza will POST to it when payment arrives:
// Express.js webhook handler
app.post("/webhooks/myaza", express.json(), async (req, res) => {
const event = req.body;
// Always respond 200 fast — process async
res.status(200).json({ received: true });
// Find the order by deposit address
const order = await db.orders.findOne({
depositAddress: event.address,
chain: event.chain,
});
if (!order) return;
// Verify amount matches
const expectedAmount = parseFloat(order.cryptoAmount);
const receivedAmount = parseFloat(event.amount);
if (receivedAmount >= expectedAmount) {
await db.orders.update(order.id, {
status: "paid",
txHash: event.txId,
paidAt: new Date(),
});
// Trigger fulfillment
await fulfillOrder(order.id);
}
});
Always respond 200 OK immediately, then process the webhook asynchronously.
If you take too long to respond, Myaza will retry the delivery.
Step 4: Handle Polling (Fallback)
For resilience, also poll session status directly in case webhooks fail:
// Poll every 5 seconds while customer is on the payment page
async function pollPaymentStatus(sessionId) {
const interval = setInterval(async () => {
const session = await fetch(
`https://secureapi.gridlog.io/api/v1/pos/sessions/${sessionId}`,
{ headers: { "X-API-Key": process.env.MYAZA_API_KEY } },
).then((r) => r.json());
if (session.status === "completed") {
clearInterval(interval);
showSuccessScreen(session.txHash);
} else if (session.status === "expired") {
clearInterval(interval);
showExpiredScreen();
}
}, 5000);
// Stop polling after session expiry
setTimeout(() => clearInterval(interval), 15 * 60 * 1000);
}
Step 5: Handle Expired Sessions
If the session expires before payment, create a new one:
if (session.status === "expired") {
const newSession = await createPaymentSession(order);
// Show new QR code to customer
}
Testing
Use a testnet (Polygon Mumbai, Solana Devnet) to test end-to-end without real funds. Ask the Myaza team to enable testnet mode on your account.
Checklist
API key stored in environment variables, not code
Webhook endpoint responds 200 immediately before processing
Amount validation: received >= expected (account for rounding)
Idempotency: handle duplicate webhook deliveries gracefully
Fallback polling in case webhooks fail
Session expiry handling with new session creation
Testnet testing before going live