← บทความทั้งหมด
บทช่วยสอน

ผมโยนเอกสาร API ให้ Claude Code แล้วมันสร้าง Zalo Webhook Receiver ให้เลย — UnifyPort

Zalo ไม่มี DM API สาธารณะ ถ้าคุณกำลังสร้างผลิตภัณฑ์สำหรับตลาดเวียดนาม — หรือทีม cross-border ที่ต้องสื่อสารกับลูกค้าเวียดนาม — นี่คือกำแพงแรกที่คุณจะชนเข้า ทางเลือกคือสมัคร Official Account แต่นั่นเป็นระบบ broadcast: ยืนยันตัวตนธุรกิจ, เทมเพลตข้อความ, หน้าต่างการสนทนา, คิดค่าบริการต่อข้อความ ถ้าคุณแค่ต้องการรับข้อความที่ลูกค้าส่งมาก่อน OA กำลังแก้ปัญหาที่ใหญ่กว่าความต้องการจริงของคุณมาก

เลยลองอีกวิธีหนึ่ง เปิด Claude Code วาง webhook reference ของ UnifyPort ใน context แล้วบอกให้เขียน Zalo webhook receiver ด้วย Python ห้าสิบนาทีต่อมา ได้ Flask server ที่ยืนยันลายเซ็นทุกครั้งที่ส่งมา, บันทึกข้อความ Zalo ลง JSON file แบบมีโครงสร้าง, แล้วส่งต่อไปยัง Slack channel โค้ดทั้งหมดประมาณ 60 บรรทัด

สิ่งที่คุณจะได้ตอนจบ

Python Flask server ที่ทำสิ่งต่อไปนี้:

  1. รับ event message.received จาก webhook ของ UnifyPort
  2. ยืนยันลายเซ็นทุก delivery ด้วย HMAC-SHA256 กับ signing_secret
  3. บันทึกทุกข้อความ Zalo ลง JSON file แบบมีโครงสร้าง
  4. ส่งสรุปข้อความไปยัง Slack channel ผ่าน incoming webhook

เวลาที่ใช้: ไม่ถึงชั่วโมง ส่วนใหญ่ Claude Code ทำ คุณต้องมี UnifyPort workspace ที่เชื่อมต่อ Zalo account แล้ว (สแกน QR code — ไม่ต้องรหัสผ่าน ไม่ต้องยืนยันธุรกิจ) และ Slack incoming webhook URL

เตรียมตัว: ใส่เอกสารเข้า context ของ Claude Code

ก่อน prompt ให้ใส่ API reference เข้าไปใน context ของ agent ก่อน Claude Code อ่านไฟล์ local ได้โดยตรง วิธีที่เร็วที่สุดคือ:

  1. ก๊อปปี้ webhook event reference — โดยเฉพาะโครงสร้าง event message.received, ส่วนการยืนยันลายเซ็น, และ endpoint POST /v1/webhook-endpoints — ลงไฟล์ unifyport-reference.md ที่ root ของ project
  2. หรือวางส่วนที่เกี่ยวข้องตรงๆ ในหน้าต่างแชทตอนเริ่มต้น

สิ่งที่ Claude Code ต้องเห็น: โครงสร้าง payload ของ message.received, header x-unifyport-signature, และกลไกยืนยัน HMAC-SHA256 กับ signing_secret ถ้าขาดไป agent จะเดาชื่อ field — แล้วก็เดาผิดแน่นอน

เครื่องมืออื่นก็เหมือนกัน ใน Cursor ใช้ @Docs ชี้ไปที่ reference ใน Windsurf ใช้ docs panel กลไกเหมือนกัน: ใส่ชื่อ field จริงเข้าไป ได้โค้ดที่ถูกต้องออกมา

สร้างทีละขั้น prompt ต่อ prompt

Prompt แรก — โครงสร้าง:

อ่าน unifyport-reference.md เขียน Flask server ใน app.py พร้อม route POST /webhook เมื่อ field event เป็น “message.received” ให้ print provider, from, text ออก stdout ทุก event ตอบ 200

Claude Code อ่าน reference แล้วสร้าง:

from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route("/webhook", methods=["POST"])
def webhook():
    evt = request.get_json()
    if evt.get("event") == "message.received":
        print(f"[{evt['provider']}] {evt['from']}: {evt['text']}")
    return "", 200

if __name__ == "__main__":
    app.run(port=3000)

เจ็ดบรรทัดของ handler เพราะโครงสร้าง event เป็นมาตรฐานเดียวกัน เช็คแค่ field เดียว — ไม่ต้อง parse แยกตาม platform

Prompt ที่สอง — ยืนยันลายเซ็น:

ทุก webhook delivery จะมี header x-unifyport-signature ยืนยันด้วย HMAC-SHA256 โดยใช้ signing_secret จาก environment variable HMAC ต้องคำนวณจาก raw request body ไม่ใช่ JSON ที่ serialize ใหม่ ถ้ายืนยันไม่ผ่านตอบ 401 ใช้ constant-time comparison

ขั้นตอนนี้เปลี่ยนของเล่นให้เป็นของจริงที่ใช้ production ได้ Claude Code implement ถูกเพราะอ่าน reference แล้ว:

import hmac
import hashlib
import os

SIGNING_SECRET = os.environ["UNIFYPORT_SIGNING_SECRET"]

def verify_signature(req):
    signature = req.headers.get("x-unifyport-signature", "")
    expected = hmac.new(
        SIGNING_SECRET.encode(),
        req.get_data(),
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(signature, expected)

req.get_data() คืนค่า raw bytes ก่อนที่ Flask จะ parse JSON — รายละเอียดนี้สำคัญมาก ถ้า agent ใช้ request.get_json() แล้ว serialize ใหม่ HMAC จะไม่มีวันตรงกัน prompt เขียนว่า “raw request body” มันเขียนถูกตั้งแต่ครั้งแรก

Prompt ที่สาม — log แบบมีโครงสร้างและส่งต่อ Slack:

เพิ่มสองอย่าง: (1) append ทุก event message.received ลงไฟล์ messages.jsonl บรรทัดละ JSON object มี timestamp, provider, from, text (2) POST สรุปหนึ่งบรรทัดไปยัง Slack incoming webhook จาก environment variable SLACK_WEBHOOK_URL

Claude Code จัดการทั้งสองอย่างในรอบเดียว app.py ฉบับสมบูรณ์:

import hmac
import hashlib
import json
import os
import requests
from flask import Flask, request

app = Flask(__name__)
SIGNING_SECRET = os.environ["UNIFYPORT_SIGNING_SECRET"]
SLACK_WEBHOOK_URL = os.environ.get("SLACK_WEBHOOK_URL", "")

def verify_signature(req):
    signature = req.headers.get("x-unifyport-signature", "")
    expected = hmac.new(
        SIGNING_SECRET.encode(),
        req.get_data(),
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(signature, expected)

@app.route("/webhook", methods=["POST"])
def webhook():
    if not verify_signature(request):
        return "", 401

    evt = request.get_json()
    if evt.get("event") == "message.received":
        record = {
            "timestamp": evt["timestamp"],
            "provider": evt["provider"],
            "from": evt["from"],
            "text": evt.get("text", ""),
            "message_id": evt["message_id"],
        }
        with open("messages.jsonl", "a") as f:
            f.write(json.dumps(record) + "\n")

        if SLACK_WEBHOOK_URL:
            requests.post(SLACK_WEBHOOK_URL, json={
                "text": f"[{evt['provider']}] {evt['from']}: {evt.get('text', '')}"
            })

    return "", 200

if __name__ == "__main__":
    app.run(port=3000)

หกสิบบรรทัดโค้ด ยืนยันลายเซ็น log แบบมีโครงสร้าง ส่งต่อ Slack ครบ handler ทั้งหมดไม่ขึ้นกับ platform — evt["provider"] บอกว่าเป็น Zalo แต่โค้ดไม่เคยแยกตาม platform

เปิด server แล้วดูข้อความ Zalo เข้ามา

เชื่อมต่อ Zalo account ใน UnifyPort workspace — Zalo ใช้ QR code authentication สแกนจากแอป Zalo เชื่อมต่อได้ในไม่กี่วินาที ไม่ต้องยืนยันธุรกิจ ไม่ต้องสมัคร OA ไม่ต้องจัดการรหัสผ่าน ลงทะเบียน webhook endpoint พร้อม subscribed_events: ["message.received"] และ signing_secret

เปิด server แล้วส่งข้อความจาก Zalo contact event จะมาในรูปแบบนี้:

{
  "event": "message.received",
  "account_id": "acct_4Xn9kL",
  "provider": "zalo",
  "from": "user_7b2e4f",
  "text": "Sản phẩm này còn hàng không ạ?",
  "timestamp": 1750262400,
  "message_id": "zalo_msg_9d3a1c"
}

Server ยืนยันลายเซ็น append record ลง messages.jsonl ส่งสรุปไป Slack ถ้ายืนยันลายเซ็นล้มเหลวได้ 401 — ตรวจสอบว่า UNIFYPORT_SIGNING_SECRET ตรงกับที่ตั้งค่าใน webhook endpoint

ขยาย: เพิ่ม WhatsApp โดยไม่แก้ handler แม้แต่บรรทัดเดียว

ตรงนี้คือจุดที่ unified webhook แสดงคุณค่า เชื่อมต่อ WhatsApp account ใน workspace เดียวกัน subscribe webhook endpoint เดียวกัน route /webhook เดิมจัดการทั้งสอง platform ข้อความ WhatsApp มาในโครงสร้าง message.received เดียวกัน — ต่างแค่ field provider โครงสร้างเหมือนกันทุกอย่าง:

{
  "event": "message.received",
  "account_id": "acct_2Rm7wQ",
  "provider": "whatsapp",
  "from": "user_5c8d3a",
  "text": "Do you ship to Thailand?",
  "timestamp": 1750262460,
  "message_id": "wa_msg_6f1b2e"
}

ไม่ต้อง handler ใหม่ ไม่ต้อง conditional import ไม่ต้อง parse แยกสำหรับ WhatsApp ไฟล์ messages.jsonl ตอนนี้มีทั้งข้อความ Zalo และ WhatsApp ใน schema เดียวกัน Slack channel แสดงทั้งสอง stream เพิ่ม LINE หรือ Telegram ทีหลังก็เหมือนกัน — เชื่อมต่อ account แล้ว handler ที่มีอยู่จัดการทั้งหมด สำหรับตลาดไทย LINE เป็นช่องทางหลัก — คุณสามารถเพิ่ม LINE account แล้ว handler เดิมก็รับข้อความ LINE ได้เลยโดยไม่ต้องแก้โค้ดอะไร

ประเด็นไม่ใช่ว่า Claude Code เขียน server ให้คุณ ประเด็นคือเมื่อ API surface เรียบง่ายพอที่จะใส่ใน context ของ agent AI coding tool จะทำเสร็จสมบูรณ์ตั้งแต่ครั้งแรก โครงสร้าง event เดียว กลไกลายเซ็นเดียว endpoint ส่งเดียว — target แบบนี้ agent ที่ได้ prompt ไม่พลาด ป้อนเอกสาร UnifyPort API ให้เครื่องมือของคุณ ให้ schema message.received แล้วคุณจะได้ webhook receiver ที่ยืนยันลายเซ็น บันทึก log ส่งต่อข้อความ ก่อนมื้อเที่ยง — แล้วไม่ต้องแก้โค้ดก็เชื่อมต่อทุกช่องทางได้เลย