Casso Developer
stable (v2)
stable (v2)
  • Tổng quan
  • Kết nối Casso bằng Webhook
    • Thiết lập Webhook thủ công
    • Xử lý sự kiện Webhook
  • Sử dụng API Casso
    • Chứng thực API
      • Tạo API Key thủ công
    • Danh sách API
      • API lấy thông tin user
      • API thiết lập webhook
      • API lấy giao dịch
      • API lấy thông tin tài khoản ngân hàng
      • API buộc đồng bộ giao dịch mới ngay
  • Tài nguyên khác
    • Tích hợp xác nhận thanh toán
    • Change log
Powered by GitBook
On this page
  • Giới thiệu
  • Trước khi bắt đầu
  • Hướng dẫn tích hợp
  • Cấu trúc file sever
  • Bước 1: Tạo file index.js
  • Bước 2: Tạo file app.js, config và xử lý lỗi
  • Bước 3: Tạo các routes và test hello world
  • Bước 4: Xây dựng các hàm hỗ trợ
  • Bước 5: Xây dựng các Route
  • Cảm ơn đã theo dõi

Was this helpful?

  1. Tài nguyên khác

Tích hợp xác nhận thanh toán

Thực hành lập trình xử lý sự kiện webhook để xác nhận thanh toán

PreviousAPI buộc đồng bộ giao dịch mới ngayNextChange log

Last updated 3 months ago

Was this helpful?

Giới thiệu

Hiện tại đã hỗ trợ nhiều hình thức tích hợp xác nhận thanh toán thông qua các đã public. Để phần tích hợp thanh toán của bạn xịn hơn thì có thể dùng để tạo QR-Code cho phần thanh toán. là tiêu chuẩn quốc gia về mã QR ngân hàng. Mã này được chấp nhận bởi 50 ngân hàng Việt Nam. Có thể xem chi tiết tại

Bạn có thể dùng để xác thanh toán.

Hướng dẫn này sử dụng Tích hợp Webhook để thực hiện. Nếu bạn sử dụng Tích hợp Webhook V2 để xác nhận thanh toán, bạn vẫn có thể tham khảo hướng dẫn này. Tuy nhiên, có một số thay đổi đối với cấu trúc dữ liệu và hình thức kiểm tra tính toàn vẹn của dữ liệu webhook. Casso đã có cập nhật ở bài viết Thiết lập Webhook thủ công

Trước khi bắt đầu

Hướng dẫn tích hợp

Để có thể sử dụng và hiểu được các API này thì dưới đây Casso demo một server basic được viết bằng + basic về tích hợp thanh toán. Chi tiết source tại .

Dưới đây là demo các bước về việc tạo webhook để lắng nghe có các giao dịch mới của Casso gửi qua và yêu cầu đồng bộ giao dịch tức thì từ phía app. Quá trình code có thể chỉ mất vài giờ nếu bạn đã quen với việc viết API. Bạn có thể làm theo kịch bản sau:

Cấu trúc file sever

Bước 1: Tạo file index.js

Đầu tiên chúng ta sẽ tạo file index.js để xây dựng server lắng nghe các request. Server mình sẽ thiết lập với cổng 4300

require('dotenv').config({ path: '.env' });
let app = require('./app');
async function main() {
    app
    console.log(`Server on port ${process.env.PORT || 4300}`);
};
main();

Bước 2: Tạo file app.js, config và xử lý lỗi

let express = require("express");
var cors = require('cors');
let app = express();
// Tạo cors
var corsOption = {
    origin: true,
    methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
    credentials: true,
    exposedHeaders: ['x-auth-token']
  };
app.use(cors(corsOption));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use('/', require('./routes'));
// Endpoint not found
app.use(function (req, res, next) {
    res.status(404).json({
        code: 404,
        message: 'Endpoint not found'
    });
})
// Xử lí khi lỗi ở phía server
app.use(function (err, req, res, next) {
    res.status(500).json({
        code: 500, error: 'Something went wrong, please try again!'
    })
})
app.listen(process.env.PORT || 4300);
module.exports = app;

Bước 3: Tạo các routes và test hello world

Ở đây mình sẽ tạo 3 route chính:

  • /webhook/handler-bank-transfer Webhook để nhận thông tin giao dịch từ Casso

  • /register-webhook Thực hiện đăng kí webhook và lấy token từ Casso

  • /users-paid Thực hiện tính năng đồng bộ giao dịch tức thì qua Casso

var express = require('express');
var router = express.Router();
//Router này sẽ là webhook nhận thông tin giao dịch từ casso gọi qua được bảo mật bằng secure_token trong header
router.route('/webhook/handler-bank-transfer')
    .post(async (req, res, next) => {
        res.status(200).json({message: "Hello world"})
    })
// Router này sẽ thực hiện tính năng đồng bộ giao dịch tức thì.
// Ví dụ: Khi người dùng chuyển khoản cho bạn và họ ấn nút tôi đã thanh toán thì nên xử lí gọi qua casso đề đồng bộ giao dịch vừa chuyển khoản
router.route('/users-paid')
    .post(async (req, res, next) => {
        res.status(200).json({message: "Hello world"})
    })
// Route này sẽ thực hiện đăng kí webhook dựa vào API KEY và lấy thông tin về business và banks
router.route('/register-webhook')
    .post(async (req, res, next) => {
        res.status(200).json({message: "Hello world"})
    })

module.exports = router;

Kiểm tra với postman

Bước 4: Xây dựng các hàm hỗ trợ

//utils/api.js
const axios = require("axios");
const queryString =  require("query-string");

const axiosClient = axios.create({
  baseURL: 'https://oauth.casso.vn/v2',
  headers: {
    "content-type": "application/json",
    "Authorization": `Apikey ${api_key}`,
  },
  paramsSerializer: (params) => queryString.stringify(params),
});
axiosClient.interceptors.request.use(async (config) => {
  return config;
});
axiosClient.interceptors.response.use(
  (response) => {
    if (response && response.data) return response.data;
    return response;
  },
  (error) => {
    throw error;
  }
);
module.exports =  axiosClient;

Mẹo: Bạn có thể thay thế giá trị của Authorization với Bearer + access token nhận được từ xác thực Oauth 2.0 của Casso.

Sau khi code HTTP Client thì tiến hành dựng từng hàm tương ứng với từng API.

/*utils/get_user_info.util.js*/
    getDetailUser: async () => {
        let res = await api.get(`/userInfo`);
        return res;
    },
/*utils/sync.util.js*/
    syncTransaction: async (bankNumber, apiKey) => {
        let res = await api.post('/sync', { bank_acc_id: bankNumber });
        return res;
    }
/*webhook.util.js*/
    create: async (data) => {
        let res = await api.post('/webhooks', data);
        return res;
    },
    getDetailWebhookById: async (webhookId) => {
        let res = await api.get(`/webhooks/${webhookId}`);
        return res;
    },
    updateWebhookById: async (webhookId, data) => {
        let res = await api.put(`/webhooks/${webhookId}`, data);
        return res;
    },
    deleteWebhookById: async (webhookId) => {
        let res = await api.delete(`/webhooks/${webhookId}`);
        return res;
    },
    deleteWebhookByUrl: async (urlWebhook) => {
        // Thêm url vào query để delete https://oauth.casso.vn/v1/webhooks?webhook=https://website-cua-ban.com/api/webhook
        let query = { params: { webhook: urlWebhook } };
        let res = await api.delete(`/webhooks`, query);
        return res;
    },
  • Parser orderId từ nội dung giao dịch và tiền tố giao dịch (DH1231=> 1231) và đồng thời cũng kiểm tra có phân biệt chữ hoa với thường trong nội dung giao dịch hay không?

/*webhook.util.js*/
    parseOrderId: (caseInsensitive, transactionPrefix, description) => {
        // Ở đây mình ở sử dụng regex để parse nội dung chuyển khoản có chứa orderId
        // CASSO101 => orderId = 101
        let re = new RegExp(transactionPrefix);
        if (!caseInsensitive)
            re = new RegExp(transactionPrefix, 'i');
        let matchPrefix = description.match(re);
        // Không tồn tại tiền tố giao dịch
        if (!matchPrefix) return null;
        let orderId = parseInt(description.substring(transactionPrefix.length, description.length));
        return orderId;
    }

Bước 5: Xây dựng các Route

Mình cần định nghĩa một vài biến cần trong quá trình dựng

//routes/index.js
//Tiền tố giao dịch
const transaction_prefix = 'CASSO';
// Phân biệt chữ hoa/thường trong tiền tố giao dịch
const case_insensitive = false;
//Hạn của đơn hàng là 3 ngày. Quá 3 ngày thì không xử lý
const expiration_date = 3;
// API KEY lấy từ casso
const api_key = '45e40320-e0b7-11eb-a12c-35cc867f21a0';
// secure_token đăng kí khi tạo webhook
const secure_token = 'R5G4cbnN7uSAwfTd'
  1. Route tạo webhook bằng API_KEY và lấy thông tin user bao gồm Business và banks

//routes/index.js
router.route('/register-webhook')
    .post(async (req, res, next) => {
        try {
            //Delete Toàn bộ webhook đã đăng kí trước đó với https://ten-mien-cua-ban.com/webhook/handler-bank-transfer
            await webhookUtil.deleteWebhookByUrl('https://ten-mien-cua-ban.com/webhook/handler-bank-transfer');
            //Tiến hành tạo webhook
            let data = {
                webhook: 'https://ten-mien-cua-ban.com/webhook/handler-bank-transfer',
                secure_token: secure_token,
                income_only: true
            }
            let newWebhook = await webhookUtil.create(data);
            // Lấy thông tin về userInfo
            let userInfo = await userUtil.getDetailUser();
            return res.status(200).json({
                code: 200,
                message: 'success',
                data: {
                    webhook: newWebhook.data,
                    userInfo: userInfo.data
                }
            })
        } catch (error) {
            next(error)
        }
    })
curl --location --request POST 'http://localhost:4300/register-webhook' \
--header 'Content-Type: application/json'
{
    "code": 200,
    "message": "success",
    "data": {
        "webhook": {
            "id": 415,
            "channel": "webhook",
            "param1": "https://ten-mien-cua-ban.com/webhook/handler-bank-transfer",
            "param2": "R5G4cbnN7uSAwfTd",
            "sendOnlyIncome": 1
        },
        "userInfo": {
            "user": {
                "id": 1553,
                "email": "haonh@magik.vn"
            },
            "business": {
                "id": 1540,
                "name": "Hữu Hảo"
            },
            "bankAccs": [
                {
                    "id": 619,
                    "bank": {
                        "bin": 970416,
                        "codeName": "acb_digi"
                    },
                    "bankAccountName": null,
                    "bankSubAccId": "17271687",
                    "connectStatus": 1,
                    "planStatus": 1
                },
                {
                    "id": 623,
                    "bank": {
                        "bin": 970454,
                        "codeName": "timoplus"
                    },
                    "bankAccountName": null,
                    "bankSubAccId": "8007041023848",
                    "connectStatus": 1,
                    "planStatus": 0
                }
            ]
        }
    }
}

2. Route này sẽ thực hiện tính năng đồng bộ giao dịch qua Casso.

Ví dụ: Khi người dùng chuyển khoản cho bạn và họ ấn nút tôi đã thanh toán thì nên xử lí gọi qua Casso để đồng bộ giao dịch vừa được chuyển khoản. Có thể sử dụng cho tính năng Tôi đã thanh toán để xác nhận thanh toán ngay.

//routes/index.js
router.route('/users-paid')
    .post(async (req, res, next) => {
        try {
            // Để thực hiện tính năng đồng bộ cần có Số tài khoản, Bạn có thể validate bằng schema ở middlewares
            // Hoặc có thể kiểm tra trong đây luôn
            if (!req.body.accountNumber) {
                return res.status(404).json({
                    code: 404,
                    message: 'Not foung Account number'
                })
            }
            // Tiến hành gọi hàm đồng bộ qua casso
            await syncUtil.syncTransaction(req.body.accountNumber);
            return res.status(200).json({
                code: 200,
                message: 'success',
                data: null
            })
        } catch (error) {
            next(error)
        }

    })

3. Tạo một webhook để Casso có thể gửi giao dịch qua khi có giao dịch mới (quan trọng):

//routes/index.js
router.route('/webhook/handler-bank-transfer')
    .post(async (req, res, next) => {
        try {
            // B1: Ở đây mình sẽ thực hiện check secure-token. Bình thường phần này sẽ nằm trong middlewares
            // Mình sẽ code trực tiếp tại đây cho dễ hình dung luồng. Nếu không có secure-token hoặc sai đều trả về lỗi
            if (!req.header('secure-token') || req.header('secure-token') != secure_token) {
                return res.status(401).json({
                    code: 401,
                    message: 'Missing secure-token or wrong secure-token'
                })
            }
            // B2: Thực hiện lấy thông tin giao dịch 
            for (let item of req.body.data) {
                // Lấy thông orderId từ nội dung giao dịch
                let orderId = webhookUtil.parseOrderId(case_insensitive, transaction_prefix, item.description);
                // Nếu không có orderId phù hợp từ nội dung ra next giao dịch tiếp theo
                if (!orderId) continue;
                // Kiểm tra giao dịch còn hạn hay không? Nếu không qua giao dịch tiếp theo
                if ((((new Date()).getTime() - (new Date(item.when)).getTime()) / 86400000) >= expiration_date) continue;
                // Bước quan trọng đây.
                // Sau khi có orderId Thì thực hiện thay đổi các trang thái giao dịch
                // Ví dụ như kiểm tra orderId có tồn tại trong danh sách các đơn hàng của bạn?
                // Sau đó cập nhật trạng thái theo orderId và amount nhận được: đủ hay thiếu tiền...
                // Và một số chức năng khác có thể tùy biến
            }
            return res.status(200).json({
                code: 200,
                message: 'success',
                data: null
            })
        } catch (error) {
            next(error)
        }
    })

Cảm ơn đã theo dõi

Các thứ cần thiết cho server như: cors, json, urlencodedvà

Để có thể giao tiếp với server Casso sẽ dùng 1 để gọi qua. Ở Demo này sẽ sử dụng và .

Bạn thể , để lấy API Key của bạn trên Casso.

Get userInfo bao gồm thông tin về business và banks. Mô tả cụ thể về API tại

Đồng bộ dữ liệu mới nhất. Mô tả cụ thể về API tại

Các hàm thêm, xóa, sửa và xóa webhook Mô tả chi tiết tại

Express error handling
HTTP Client
Axios
Query-string
tham khảo tài liệu này
đây
đây
đây
Casso
API
Casso
VietQR
VietQR
đây
payOS by Casso
NodeJS
Express
Github