Payment completion notice
An example where your server notifies the chat room through the server REST API (v2) when a payment completes, and a page script receives it and shows it in the chat window.
Last updated October 19, 2026
The moment a payment completes on your server, show a "purchase complete" notice to everyone in the chat room. Your server sends data to the payment channel with the Server REST API, and the page script checks that it came from the server before showing it in the chat window.
Flow
- The payment provider tells your server the payment is complete (your server's existing handling).
- Your server sends to the
paymentchannel withPOST https://api.uchat.io/v2/rooms/{id}/events. - The data arrives on the
paymentchannel in the page of everyone currently in the chat room. - The page script checks that the sender's permission is
4(server) and shows it withroom.print.
Server: Node.js
This uses the built-in fetch in Node.js 18 or later. Issue the API key and find the API chat room id (room_…) in the API menu of the admin page, and keep the key only in your server's environment variables. The key needs the Events permission.
// notify-payment.mjs
const ROOM = 'room_xxxxxxxxxxxxxxxx'; // API chat room id
export async function notifyPayment(productName) {
const res = await fetch(`https://api.uchat.io/v2/rooms/${ROOM}/events`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.UCHAT_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ plugin: 'payment', data: ['paid', productName] }),
});
const result = await res.json();
if (!res.ok) {
// 429 chat_rate_limited: free chat room send limit exceeded, etc.
console.warn('uchat notify failed', res.status, result.error);
}
return result;
}
// Inside your payment completion handling
// await notifyPayment('Premium pass');
Server: PHP
<?php
function notify_payment($product_name) {
$ch = curl_init('https://api.uchat.io/v2/rooms/room_xxxxxxxxxxxxxxxx/events');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('UCHAT_API_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode(['plugin' => 'payment', 'data' => ['paid', $product_name]], JSON_UNESCAPED_UNICODE),
CURLOPT_TIMEOUT => 5,
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
$result = json_decode($body, true);
if ($status >= 400) {
error_log('uchat notify failed: ' . $status . ' ' . ($result['error']['code'] ?? 'no response'));
}
return $result;
}
// Inside your payment completion handling
// notify_payment('Premium pass');
On success, it returns HTTP 202 and {"data":{"sent":true}}. It succeeds even when no one is in the chat room (there is just no one to receive it). On failure, you get an HTTP status and {"error":{"code":…,"message":…}}. For the full list of error codes, see Server REST API.
Page: receive and display
<script>U=window.U=window.U||{},U.events=U.events||[],U.chat=function(n){return{on:function(e,t){U.events.push([n,e,t])},off:function(e){for(var t=U.events.length;t>0;)U.events[--t][0]==n&&U.events[t][1]==e&&U.events.splice(t,1)}}};</script>
<script async src="https://client.uchat.io/uchat.js"></script>
<u-chat room="my-room" style="display:inline-block;width:100%;max-width:400px;height:500px"></u-chat>
<script>
U.chat('my-room').on('after.create#payment', function (room) {
function esc(value) {
return String(value).replace(/[&<>"']/g, function (c) {
return '&#' + c.charCodeAt(0) + ';';
});
}
room.plugin.add('payment').onReceived(function (room, data) {
// Trust only what the server API sent. Users can send to the same channel too
if (!data.user || data.user.auth !== 4) return;
if (data.type !== 0) return; // Skip saved values that arrive on join (type 1)
var msg = data.data;
if (msg[0] !== 'paid' || typeof msg[1] !== 'string') return;
room.print('🎉 Someone just bought <b>' + esc(msg[1].slice(0, 50)) + '</b>!');
});
});
</script>
Things to note
room.printdisplays HTML, so always escape received values as in the code above.- If you display without checking permissions, any user can show fake payment notices through the same channel.
data.user.auth === 4is set only on data sent through the server API or values saved by a moderator or higher. Saved values arrive withtype1 on join, so the code above accepts onlytype0 (sent to everyone through the server API). Server API data sent to a specific nickname is alsotype1, so it can't be told apart from saved values. - The start of the REST request body and the request IP are kept for 30 days in the call log of the API menu on the admin page. Do not send personal information such as the buyer's name, contact details, or card details.
- Data sent without
savereaches only people connected now. The API responds with success even if no one is in the chat room, and even if a call fails, don't fail the payment itself; just log it. A payment notice is about something that already happened, so it should not be saved withsave. - To send to one person only, put
"to":"nickname"in the body (Korean nicknames work too). Data sent this way arrives astype1, so remove thetypecheck from the code above. - In free chat rooms, you get HTTP 429
chat_rate_limitedif the total number of recipients over the last 60 seconds exceeds the limit (Channel API and limits). - To just show it in the chat room without code, turn on the Event alert box plugin and send with
pluginset toalertBoxanddataset to["custom", "Purchase complete", product name]. - The general flow for sending server notifications is in Sending notifications from your server to a chat room.
Still not sure how it works? Ask your question on the contact board.