logo

Lộ trình

Khóa học

Tài liệu

Mock Interview

Liên hệ

Quay lại
  • Trang chủ

    /

  • Tài liệu

    /

  • Hướng dẫn xây dựng Video Call Webapp đơn giản với WebRTC
Tài liệu

Hướng dẫn xây dựng Video Call Webapp đơn giản với WebRTC

Ronin Engineer

28 Tháng 11 2024

<p>by @thanbv1510</p><h2 id="m%E1%BB%A5c-l%E1%BB%A5c">Mục lục</h2><ol><li><a>Giới thiệu về WebRTC</a></li><li><a>Sequence Diagram</a></li><li><a>Hướng dẫn code chi tiết</a></li><li><a>Demo</a></li><li><a>Tổng kết và hướng phát triển</a></li></ol><h2 id="1-gi%E1%BB%9Bi-thi%E1%BB%87u-v%E1%BB%81-webrtc">1. Giới thiệu về WebRTC</h2><h3 id="webrtc-l%C3%A0-g%C3%AC">WebRTC là gì?</h3><figure class="kg-card kg-image-card"><img src="https://gist.github.com/user-attachments/assets/28d764a6-65d7-4331-8063-3ea27b8462c3" class="kg-image" alt="Untitled-2024-11-27-1959(2)" loading="lazy" width="2241" height="1467"></figure><p>WebRTC (Web Real-Time Communication) là một công nghệ mã nguồn mở cho phép truyền thông theo thời gian thực (real-time) giữa các trình duyệt web và ứng dụng di động thông qua giao thức peer-to-peer (P2P), không cần cài đặt plugin hay phần mềm bổ sung.<br>Kiến trúc WebRTC bao gồm các thành phần chính sau:</p><ol><li><strong>Signaling Server</strong><ul><li>Đóng vai trò trung gian kết nối giữa các peer</li><li>Hỗ trợ trao đổi thông tin về kết nối như địa chỉ IP, ports, và khả năng media</li><li>Không trực tiếp xử lý dữ liệu media</li></ul></li><li><strong>NAT (Network Address Translation)</strong><ul><li>Chuyển đổi địa chỉ IP private thành public để có thể giao tiếp qua Internet</li><li>Là một trong những thách thức chính trong việc thiết lập kết nối P2P</li></ul></li><li><strong>STUN/TURN Server</strong><ul><li>STUN (Session Traversal Utilities for NAT): Giúp peer xác định địa chỉ IP public và loại NAT</li><li>TURN (Traversal Using Relays around NAT): Làm relay server khi không thể thiết lập kết nối P2P trực tiếp</li></ul></li><li><strong>Peers (End-points)</strong><ul><li>Các thiết bị đầu cuối (máy tính, điện thoại) tham gia vào cuộc gọi</li><li>Trực tiếp trao đổi media streams (audio, video, data) qua kết nối P2P</li></ul></li></ol><h3 id="c%C3%A1c-th%C3%A0nh-ph%E1%BA%A7n-k%E1%BB%B9-thu%E1%BA%ADt-ch%C3%ADnh-c%E1%BB%A7a-webrtc">Các thành phần kỹ thuật chính của WebRTC</h3><ol><li><strong>MediaStream (getUserMedia)</strong><ul><li>Cho phép truy cập vào camera và microphone của thiết bị</li><li>Tạo luồng media (audio/video) để truyền tải</li><li>Hỗ trợ các constraints để cấu hình chất lượng media</li></ul></li><li><strong>RTCPeerConnection</strong><ul><li>Xử lý việc thiết lập kết nối P2P giữa các clients</li><li>Quản lý việc truyền tải media streams</li><li>Xử lý mã hóa và giải mã dữ liệu</li><li>Quản lý băng thông và chất lượng kết nối</li></ul></li><li><strong>RTCDataChannel</strong><ul><li>Cho phép truyền tải dữ liệu tùy ý giữa các peers</li><li>Hỗ trợ cả reliable và unreliable data transmission</li><li>Thích hợp cho chat, file sharing, game data, etc.</li></ul></li></ol><h2 id="2-sequence-diagram">2. Sequence Diagram</h2><figure class="kg-card kg-image-card"><img src="https://roninhub.com/content/images/2024/11/sequence_diagram.png" class="kg-image" alt="" loading="lazy" width="643" height="791" srcset="https://roninhub.com/content/images/size/w600/2024/11/sequence_diagram.png 600w, https://roninhub.com/content/images/2024/11/sequence_diagram.png 643w"></figure><h2 id="3-h%C6%B0%E1%BB%9Bng-d%E1%BA%ABn-code-chi-ti%E1%BA%BFt">3. Hướng dẫn code chi tiết</h2><h3 id="31-c%E1%BA%A5u-tr%C3%BAc-project">3.1 Cấu trúc project</h3><p>Đầu tiên, chúng ta sẽ tạo một project Spring boot 3 với 2 depencency là <code>Spring Web</code> và <code>WebSocket</code></p><pre><code>src/ ├── main/ │ ├── java/ │ │ └── com/roninhub/webrtc/ │ │ ├── WebrtcApplication.java │ │ ├── WebSocketConfiguration.java │ │ └── SocketHandler.java │ ├── resources/ │ │ └── static/ │ │ ├── index.html │ │ └── main.js </code></pre><h3 id="32-backend-v%E1%BB%9Bi-spring-boot">3.2 Backend với Spring Boot</h3><h4 id="webrtcapplicationjava">WebrtcApplication.java</h4><pre><code class="language-java">@SpringBootApplication public class WebrtcApplication { public static void main(String[] args) { SpringApplication.run(WebrtcApplication.class, args); } } </code></pre><h4 id="websocketconfigurationjava">WebSocketConfiguration.java</h4><pre><code class="language-java">@Configuration @EnableWebSocket public class WebSocketConfiguration implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(new SocketHandler(), "/socket") .setAllowedOrigins("*"); } } </code></pre><p>Mục đích của file <code>WebSocketConfiguration.java</code>là:</p><ul><li><code>@EnableWebSocket</code>: Kích hoạt hỗ trợ WebSocket trong ứng dụng</li><li><code>registerWebSocketHandlers</code>: Đăng ký endpoint "/socket" và cho phép CORS</li></ul><h4 id="sockethandlerjava">SocketHandler.java</h4><pre><code class="language-java">@Component public class SocketHandler extends TextWebSocketHandler { List&lt;WebSocketSession&gt; sessions = new CopyOnWriteArrayList&lt;&gt;(); @Override public void handleTextMessage(WebSocketSession session, TextMessage message) throws InterruptedException, IOException { for (WebSocketSession webSocketSession : sessions) { if (webSocketSession.isOpen() &amp;&amp; !session.getId().equals(webSocketSession.getId())) { webSocketSession.sendMessage(message); } } } @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { sessions.add(session); } } </code></pre><p>Trong file <code>SocketHandler.java</code> sẽ thực hiện các nhiệm vụ:</p><ul><li>Quản lý danh sách các WebSocket sessions</li><li>Xử lý các message và chuyển tiếp tới các clients khác</li><li>Thêm session mới khi có kết nối được thiết lập</li></ul><h3 id="33-frontend-v%E1%BB%9Bi-html-v%C3%A0-javascript">3.3 Frontend với HTML và JavaScript</h3><h4 id="indexhtml">index.html</h4><pre><code class="language-html">&lt;!DOCTYPE html&gt; &lt;html&gt; &lt;head&gt; &lt;title&gt;WebRTC Video Call&lt;/title&gt; &lt;style&gt; video { width: 300px; height: 200px; margin: 10px; background: #ddd; } &lt;/style&gt; &lt;/head&gt; &lt;body&gt; &lt;h1&gt;WebRTC Video Call&lt;/h1&gt; &lt;div&gt; &lt;h3&gt;Remote video&lt;/h3&gt; &lt;video id="remoteVideo" autoplay playsinline&gt;&lt;/video&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;Local video&lt;/h3&gt; &lt;video id="localVideo" autoplay playsinline&gt;&lt;/video&gt; &lt;/div&gt; &lt;button onclick="makeCall()"&gt;Make Call&lt;/button&gt; &lt;script src="main.js"&gt;&lt;/script&gt; &lt;/body&gt; &lt;/html&gt; </code></pre><h4 id="mainjs">main.js</h4><pre><code class="language-javascript">const websocket = new WebSocket('ws://localhost:8080/socket'); let peerConnection; let localStream; let remoteStream; const localVideo = document.getElementById('localVideo'); const remoteVideo = document.getElementById('remoteVideo'); websocket.onopen = function () { const configuration = null; peerConnection = new RTCPeerConnection(configuration); const constraints = {audio: true, video: {width: 100, height: 70}}; navigator.mediaDevices.getUserMedia(constraints) .then(stream =&gt; { localStream = stream; localVideo.srcObject = stream; stream.getTracks().forEach(track =&gt; { peerConnection.addTrack(track, stream); }); localVideo.onloadedmetadata = () =&gt; { localVideo.play(); }; }) .catch(error =&gt; { console.error('Error accessing media devices.', error); }); peerConnection.onicecandidate = function (event) { if (event.candidate) { send({ event: "candidate", data: event.candidate }); } }; peerConnection.ontrack = function (event) { if (!remoteStream) { remoteStream = new MediaStream(); remoteVideo.srcObject = remoteStream; } remoteStream.addTrack(event.track); remoteVideo.onloadedmetadata = () =&gt; { remoteVideo.play(); }; }; }; websocket.onmessage = function (msg) { const content = JSON.parse(msg.data); const data = content.data; switch (content.event) { case "offer": handleOffer(data); break; case "answer": handleAnswer(data); break; case "candidate": handleCandidate(data); break; default: break; } }; function handleOffer(offer) { peerConnection.setRemoteDescription(new RTCSessionDescription(offer)) .then(() =&gt; peerConnection.createAnswer()) .then(answer =&gt; peerConnection.setLocalDescription(answer)) .then(() =&gt; send({event: "answer", data: peerConnection.localDescription})) .catch(error =&gt; console.error('Error handling offer:', error)); } function handleAnswer(answer) { peerConnection.setRemoteDescription(new RTCSessionDescription(answer)) .then(() =&gt; console.log("Connection established successfully!")); } function handleCandidate(candidate) { peerConnection.addIceCandidate(new RTCIceCandidate(candidate)) .then(() =&gt; console.log("Candidate added successfully!")); } function send(message) { websocket.send(JSON.stringify(message)); } function makeCall() { peerConnection.createOffer() .then(offer =&gt; peerConnection.setLocalDescription(offer)) .then(() =&gt; send({event: "offer", data: peerConnection.localDescription})) .catch(error =&gt; console.error('Error creating offer:', error)); } </code></pre><p>Giải thích code:</p><ol><li>Khởi tạo WebSocket connection tới server</li><li>Thiết lập RTCPeerConnection và lấy media stream từ camera/mic</li><li>Xử lý các events:<ul><li><code>onicecandidate</code>: Gửi ICE candidates tới peer khác</li><li><code>ontrack</code>: Xử lý media stream nhận được từ peer khác</li></ul></li><li>Xử lý signaling:<ul><li><code>handleOffer</code>: Xử lý offer nhận được</li><li><code>handleAnswer</code>: Xử lý answer nhận được</li><li><code>handleCandidate</code>: Xử lý ICE candidate nhận được</li></ul></li><li>Function <code>makeCall()</code>: Tạo offer để bắt đầu cuộc gọi</li></ol><h2 id="4-demo">4. Demo</h2><figure class="kg-card kg-image-card"><img src="https://roninhub.com/content/images/2024/11/webrtc_demo.gif" class="kg-image" alt="" loading="lazy" width="854" height="480" srcset="https://roninhub.com/content/images/size/w600/2024/11/webrtc_demo.gif 600w, https://roninhub.com/content/images/2024/11/webrtc_demo.gif 854w" sizes="(min-width: 720px) 720px"></figure><h2 id="5-t%E1%BB%95ng-k%E1%BA%BFt-v%C3%A0-h%C6%B0%E1%BB%9Bng-ph%C3%A1t-tri%E1%BB%83n">5. Tổng kết và hướng phát triển</h2><p>Bài viết này giúp các bạn xây dựng được ứng dụng gọi video cơ bản với WebRTC bằng cách sử dụng Spring Boot làm signaling server, thực hiện kết nối P2P giữa hai clients và truyền tải luồng audio/video. Các bạn có thể tiếp tục phát triển thêm các tính năng nâng cao như:</p><ol><li><strong>Quản lý phòng (Room Management)</strong><ul><li>Tạo và quản lý các phòng chat</li><li>Cho phép nhiều người tham gia cùng một phòng</li></ul></li><li><strong>Bảo mật</strong><ul><li>Thêm xác thực người dùng</li><li>Mã hóa đầu cuối (end-to-end encryption)</li><li>Quản lý phiên đăng nhập</li></ul></li><li><strong>Tính năng chat</strong><ul><li>Chat text song song với video call</li><li>Chia sẻ file</li><li>Emoji và stickers</li></ul></li><li><strong>Xử lý lỗi và phục hồi</strong><ul><li>Tự động kết nối lại khi mất kết nối</li><li>Xử lý các trường hợp lỗi</li></ul></li><li><strong>Tính năng nâng cao</strong><ul><li>Chia sẻ màn hình</li><li>Ghi âm/video</li></ul></li></ol><hr><p>&lt;/&gt; Code tham khảo: <a href="https://github.com/ronin-engineer-88/webrtc-demo?ref=roninhub.com">https://github.com/ronin-engineer-88/webrtc-demo</a><br>✏️ System Design VN: <a href="https://fb.com/groups/systemdesign.vn?ref=roninhub.com">https://fb.com/groups/systemdesign.vn</a><br>📚 Đọc thêm tài liệu khác: <a href="https://roninhub.com/tai-lieu">https://roninhub.com/tai-lieu</a><br>🎬 Youtube: <a href="https://youtube.com/@ronin-engineer?ref=roninhub.com">https://youtube.com/@ronin-engineer</a><br>🎞️ TikTok: <a href="https://tiktok.com/@ronin.engineer?ref=roninhub.com">https://tiktok.com/@ronin.engineer</a></p><p></p>

by @thanbv1510

Mục lục

  1. Giới thiệu về WebRTC
  2. Sequence Diagram
  3. Hướng dẫn code chi tiết
  4. Demo
  5. Tổng kết và hướng phát triển

1. Giới thiệu về WebRTC

WebRTC là gì?

Untitled-2024-11-27-1959(2)

WebRTC (Web Real-Time Communication) là một công nghệ mã nguồn mở cho phép truyền thông theo thời gian thực (real-time) giữa các trình duyệt web và ứng dụng di động thông qua giao thức peer-to-peer (P2P), không cần cài đặt plugin hay phần mềm bổ sung.
Kiến trúc WebRTC bao gồm các thành phần chính sau:

  1. Signaling Server
    • Đóng vai trò trung gian kết nối giữa các peer
    • Hỗ trợ trao đổi thông tin về kết nối như địa chỉ IP, ports, và khả năng media
    • Không trực tiếp xử lý dữ liệu media
  2. NAT (Network Address Translation)
    • Chuyển đổi địa chỉ IP private thành public để có thể giao tiếp qua Internet
    • Là một trong những thách thức chính trong việc thiết lập kết nối P2P
  3. STUN/TURN Server
    • STUN (Session Traversal Utilities for NAT): Giúp peer xác định địa chỉ IP public và loại NAT
    • TURN (Traversal Using Relays around NAT): Làm relay server khi không thể thiết lập kết nối P2P trực tiếp
  4. Peers (End-points)
    • Các thiết bị đầu cuối (máy tính, điện thoại) tham gia vào cuộc gọi
    • Trực tiếp trao đổi media streams (audio, video, data) qua kết nối P2P

Các thành phần kỹ thuật chính của WebRTC

  1. MediaStream (getUserMedia)
    • Cho phép truy cập vào camera và microphone của thiết bị
    • Tạo luồng media (audio/video) để truyền tải
    • Hỗ trợ các constraints để cấu hình chất lượng media
  2. RTCPeerConnection
    • Xử lý việc thiết lập kết nối P2P giữa các clients
    • Quản lý việc truyền tải media streams
    • Xử lý mã hóa và giải mã dữ liệu
    • Quản lý băng thông và chất lượng kết nối
  3. RTCDataChannel
    • Cho phép truyền tải dữ liệu tùy ý giữa các peers
    • Hỗ trợ cả reliable và unreliable data transmission
    • Thích hợp cho chat, file sharing, game data, etc.

2. Sequence Diagram

3. Hướng dẫn code chi tiết

3.1 Cấu trúc project

Đầu tiên, chúng ta sẽ tạo một project Spring boot 3 với 2 depencency là Spring Web và WebSocket

src/
├── main/
│   ├── java/
│   │   └── com/roninhub/webrtc/
│   │       ├── WebrtcApplication.java
│   │       ├── WebSocketConfiguration.java
│   │       └── SocketHandler.java
│   ├── resources/
│   │   └── static/
│   │       ├── index.html
│   │       └── main.js

3.2 Backend với Spring Boot

WebrtcApplication.java

@SpringBootApplication
public class WebrtcApplication {
    public static void main(String[] args) {
        SpringApplication.run(WebrtcApplication.class, args);
    }
}

WebSocketConfiguration.java

@Configuration
@EnableWebSocket
public class WebSocketConfiguration implements WebSocketConfigurer {
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(new SocketHandler(), "/socket")
                .setAllowedOrigins("*");
    }
}

Mục đích của file WebSocketConfiguration.javalà:

  • @EnableWebSocket: Kích hoạt hỗ trợ WebSocket trong ứng dụng
  • registerWebSocketHandlers: Đăng ký endpoint "/socket" và cho phép CORS

SocketHandler.java

@Component
public class SocketHandler extends TextWebSocketHandler {
    List<WebSocketSession> sessions = new CopyOnWriteArrayList<>();

    @Override
    public void handleTextMessage(WebSocketSession session, TextMessage message)
            throws InterruptedException, IOException {
        for (WebSocketSession webSocketSession : sessions) {
            if (webSocketSession.isOpen() && !session.getId().equals(webSocketSession.getId())) {
                webSocketSession.sendMessage(message);
            }
        }
    }

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        sessions.add(session);
    }
}

Trong file SocketHandler.java sẽ thực hiện các nhiệm vụ:

  • Quản lý danh sách các WebSocket sessions
  • Xử lý các message và chuyển tiếp tới các clients khác
  • Thêm session mới khi có kết nối được thiết lập

3.3 Frontend với HTML và JavaScript

index.html

<!DOCTYPE html>
<html>
<head>
    <title>WebRTC Video Call</title>
    <style>
        video {
            width: 300px;
            height: 200px;
            margin: 10px;
            background: #ddd;
        }
    </style>
</head>
<body>
    <h1>WebRTC Video Call</h1>
    <div>
        <h3>Remote video</h3>
        <video id="remoteVideo" autoplay playsinline></video>
    </div>
    <div>
        <h3>Local video</h3>
        <video id="localVideo" autoplay playsinline></video>
    </div>
    <button onclick="makeCall()">Make Call</button>
    <script src="main.js"></script>
</body>
</html>

main.js

const websocket = new WebSocket('ws://localhost:8080/socket');

let peerConnection;
let localStream;
let remoteStream;

const localVideo = document.getElementById('localVideo');
const remoteVideo = document.getElementById('remoteVideo');

websocket.onopen = function () {
    const configuration = null;

    peerConnection = new RTCPeerConnection(configuration);

    const constraints = {audio: true, video: {width: 100, height: 70}};
    navigator.mediaDevices.getUserMedia(constraints)
        .then(stream => {
            localStream = stream;
            localVideo.srcObject = stream;

            stream.getTracks().forEach(track => {
                peerConnection.addTrack(track, stream);
            });

            localVideo.onloadedmetadata = () => {
                localVideo.play();
            };
        })
        .catch(error => {
            console.error('Error accessing media devices.', error);
        });

    peerConnection.onicecandidate = function (event) {
        if (event.candidate) {
            send({
                event: "candidate",
                data: event.candidate
            });
        }
    };

    peerConnection.ontrack = function (event) {
        if (!remoteStream) {
            remoteStream = new MediaStream();
            remoteVideo.srcObject = remoteStream;
        }
        remoteStream.addTrack(event.track);

        remoteVideo.onloadedmetadata = () => {
            remoteVideo.play();
        };
    };
};

websocket.onmessage = function (msg) {
    const content = JSON.parse(msg.data);
    const data = content.data;
    switch (content.event) {
        case "offer":
            handleOffer(data);
            break;
        case "answer":
            handleAnswer(data);
            break;
        case "candidate":
            handleCandidate(data);
            break;
        default:
            break;
    }
};

function handleOffer(offer) {
    peerConnection.setRemoteDescription(new RTCSessionDescription(offer))
        .then(() => peerConnection.createAnswer())
        .then(answer => peerConnection.setLocalDescription(answer))
        .then(() => send({event: "answer", data: peerConnection.localDescription}))
        .catch(error => console.error('Error handling offer:', error));
}

function handleAnswer(answer) {
    peerConnection.setRemoteDescription(new RTCSessionDescription(answer))
        .then(() => console.log("Connection established successfully!"));
}

function handleCandidate(candidate) {
    peerConnection.addIceCandidate(new RTCIceCandidate(candidate))
        .then(() => console.log("Candidate added successfully!"));
}

function send(message) {
    websocket.send(JSON.stringify(message));
}

function makeCall() {
    peerConnection.createOffer()
        .then(offer => peerConnection.setLocalDescription(offer))
        .then(() => send({event: "offer", data: peerConnection.localDescription}))
        .catch(error => console.error('Error creating offer:', error));
}

Giải thích code:

  1. Khởi tạo WebSocket connection tới server
  2. Thiết lập RTCPeerConnection và lấy media stream từ camera/mic
  3. Xử lý các events:
    • onicecandidate: Gửi ICE candidates tới peer khác
    • ontrack: Xử lý media stream nhận được từ peer khác
  4. Xử lý signaling:
    • handleOffer: Xử lý offer nhận được
    • handleAnswer: Xử lý answer nhận được
    • handleCandidate: Xử lý ICE candidate nhận được
  5. Function makeCall(): Tạo offer để bắt đầu cuộc gọi

4. Demo

5. Tổng kết và hướng phát triển

Bài viết này giúp các bạn xây dựng được ứng dụng gọi video cơ bản với WebRTC bằng cách sử dụng Spring Boot làm signaling server, thực hiện kết nối P2P giữa hai clients và truyền tải luồng audio/video. Các bạn có thể tiếp tục phát triển thêm các tính năng nâng cao như:

  1. Quản lý phòng (Room Management)
    • Tạo và quản lý các phòng chat
    • Cho phép nhiều người tham gia cùng một phòng
  2. Bảo mật
    • Thêm xác thực người dùng
    • Mã hóa đầu cuối (end-to-end encryption)
    • Quản lý phiên đăng nhập
  3. Tính năng chat
    • Chat text song song với video call
    • Chia sẻ file
    • Emoji và stickers
  4. Xử lý lỗi và phục hồi
    • Tự động kết nối lại khi mất kết nối
    • Xử lý các trường hợp lỗi
  5. Tính năng nâng cao
    • Chia sẻ màn hình
    • Ghi âm/video

</> Code tham khảo: https://github.com/ronin-engineer-88/webrtc-demo
✏️ System Design VN: https://fb.com/groups/systemdesign.vn
📚 Đọc thêm tài liệu khác: https://roninhub.com/tai-lieu
🎬 Youtube: https://youtube.com/@ronin-engineer
🎞️ TikTok: https://tiktok.com/@ronin.engineer

systemdesign
middle

Bài viết liên quan

Distributed Lock Là Gì? Tại Sao Nó Quan Trọng và Cách Triển Khai Với Redis

by @ AnhDH Mục lục 1. Đặt vấn đề 2. Distributed Lock 3. Triển khai Distributed Lock với Redis 4. Best practice 5. Tổng kết 6. Tham khảo Trong các hệ thống phân tán, việc đảm bảo tính nhất quán của dữ liệu (data consistency) và ngăn chặn tranh chấp tài nguyên (race condition) là một thách thức lớn, đặc biệt khi nhiều tiến trình hoặc service truy cập đồng thời vào các tài nguyên dùng chung. Một trong những giải pháp quan trọng để giải quyết vấn đề này chính là sử dụng distributed lock (k

Ronin Engineer Tích Hợp với VNPay Như Thế Nào?

Khi tích hợp với cổng thanh toán nói chung, tính hợp với VNPay nói riêng, chúng ta cần đánh giá nhiều yếu tố khi chọn giải pháp thanh toán như độ bảo mật, phí, độ ổn định, tính năng, tốc độ tích hợp, … Nắm được cả về nghiệp vụ để hiểu rõ hơn về luồng thanh toán được xử lý như nào.

Thiết Kế Hệ Thống Airbnb

Đặt Vấn Đề Hè đến, nhu cầu du lịch tăng cao, kéo theo nhu cầu về đặt nhà nghỉ, khách sạn cũng tăng cao. Đặt phòng theo cách truyền thống sẽ có quy trình như sau: chúng ta tìm kiếm khách sạn, nhà nghỉ trên báo chí, google, sau đó gọi điện để check và đặt phòng. Khi thực hiện theo cách truyền thống, du khách sẽ gặp một số vấn đề như sau: * Khó tìm được phòng đúng với yêu cầu (giá, vị trí, cơ sở vật chất, …). Lý do thứ nhất là chưa có những bộ lọc (filter) chuyên dụng. Thứ hai là nhiều nhà nghỉ,

Thiết Kế Hệ Thống Bán Vé (Ticketing System Design)

Những concert của Hà Anh Tuấn hay thậm chí concert của The Weeknd thu hút được rất rất nhiều khách giản trong nước và quốc tế. Tại thời điểm mở bán vé, hệ thống phải đương đầu với một số thử thách sau:

Thiết Kế Hệ Thống Phát Hiện Gian Lận (Rule-Based Fraud Detection System Design)

Coupon hay voucher là miếng bánh ngọt đối với những đối tượng gian lận (fraudsters). Do đó, các tổ chức tài chính, hệ thống tài chính như ngân hàng, cổng thanh toán, sàn thương mại, ví điện tử, … cần một hệ thống có thể phát hiện và cảnh báo kịp thời những hành vi gian lận hoặc bất thường.

Tất cả bài viết
logo

HỘ KINH DOANH LẬP VƯƠNG

Giấy chứng nhận đăng ký doanh nghiệp số: 8656162915-001. Cấp ngày 21/02/2024. Nơi cấp: Sở Kế hoạch và Đầu tư TP. Hà Nội

PHƯƠNG THỨC THANH TOÁN

vnpay

LIÊN HỆ

roninengineer88@gmail.com

0362228388

26 ngõ 156 Hồng Mai, Hai Bà Trưng, Hà Nội

THEO DÕI CHÚNG TÔI

Facebook

Youtube

Tiktok

CHÍNH SÁCH

Chính sách bảo mật

Chính sách thanh toán

Đổi trả/Hoàn tiền

Hướng dẫn thanh toán VNPAY

PHƯƠNG THỨC THANH TOÁN

vnpay

Ronin Engineer 2024