หน้าแรก
RUTS
PDPA Consent Hub

วิธีเชื่อมต่อ PDPA

ฝัง consent banner ในระบบของคุณด้วย <script> ตัวเดียว — รองรับทุกภาษา. เลือกแท็บตามเทคโนโลยีของระบบ

ทำงานยังไง
3 ขั้นตอน เหมือนกันทุกภาษา
ขั้น 1

ลงทะเบียนระบบ

สร้าง app ที่หน้า ระบบที่เชื่อม → ได้ appKey + subjectSecret แล้ว publish policy v1

ขั้น 2

ฝัง loader

ฝัง <script> พร้อมเซ็น subject ด้วย HMAC (ดูโค้ดตามภาษาด้านล่าง)

ขั้น 3

เสร็จ

widget เช็ค consent เดิม → โชว์ banner เมื่อต้อง → บันทึกการตัดสินใจ → ยิง event pdpa-consent

เลือกประเภทแอป — เซ็น HMAC ที่ไหน
subjectSecret เป็น server secret ห้ามขึ้น browser — จุดนี้ตัดสินว่าคำนวณ signature ตรงไหน
SSR

Next.js, PHP, Laravel, Django

เซ็น sig ตอน render หน้า แล้วฝัง tag ได้เลย

ไม่ต้องเพิ่ม endpoint

SPA

Angular, React SPA, Vue, PWA

browser ถือ secret ไม่ได้ → backend อ่าน session คืน { subject, ts, sig } ให้ SPA ฝัง

เพิ่ม 1 endpoint เล็ก ๆ

Static

SPA ไม่มี backend เลย

HMAC ทำไม่ได้ → fallback trust-origin (ส่งแค่ appKey + subject)

proof อ่อนกว่า — รับได้สำหรับ cookie consent

ก่อนเริ่ม — ตัวแปร environment
ใส่ที่ฝั่ง server ของระบบคุณ (เช่น .env / Portainer). ไม่ต้องใส่ appSecret (สงวนไว้สำหรับ server-to-server อนาคต)
.env
PDPA_HUB_URL=https://pdpa.web.rmutsv.ac.th
PDPA_APP_KEY=app_xxxxxxxx       # from the app detail page
PDPA_SUBJECT_SECRET=xxxxxxxx    # server secret — never expose to the browser
โค้ดตามภาษา / framework
เหมาะกับ Next.js (full-stack). เซ็น sig ตอน render — ไม่ต้องเพิ่ม endpoint. drop component ลง root layout
components/consent-script.tsx
import { createHmac } from "node:crypto";

// Server component — subjectSecret never reaches the browser.
export default function ConsentScript({ subject }: { subject?: string | null }) {
  const hub = process.env.PDPA_HUB_URL;
  const appKey = process.env.PDPA_APP_KEY;
  const secret = process.env.PDPA_SUBJECT_SECRET;
  if (!hub || !appKey || !secret || !subject) return null;

  const ts = Math.floor(Date.now() / 1000).toString();
  const sig = createHmac("sha256", secret).update(`${subject}|${ts}`).digest("hex");

  return (
    <script
      src={`${hub}/widget/v1/loader.js`}
      data-app-key={appKey}
      data-api={hub}
      data-subject={subject}
      data-ts={ts}
      data-sig={sig}
      async
    />
  );
}
app/layout.tsx
import ConsentScript from "@/components/consent-script";
// ...
const session = await getSession();
// inside <body>, render only when logged in:
<ConsentScript subject={session?.user?.username} />
รับผลการตัดสินใจ (เปิด/ปิด trackers)
widget ยิง window event ทุกครั้งที่มี consent (ทั้งของเก่าและที่เพิ่งกด) — ใช้เปิด/ปิด cookie ของระบบคุณ
js
window.addEventListener("pdpa-consent", (e) => {
  const c = e.detail; // { decision, categories: { NECESSARY, ANALYTICS, ... }, policyVersion }
  if (c.categories.ANALYTICS) {
    // โหลด analytics
  }
});
ให้ผู้ใช้ถอน / แก้ความยินยอม (สิทธิตาม PDPA)
PDPA กำหนดให้ผู้ใช้ถอนความยินยอมได้ทุกเมื่อ. widget แสดง ปุ่มลอย “จัดการคุกกี้” อัตโนมัติ (มุมล่างซ้าย) ในทุก app — กดแล้วเปิด banner กลับมาแก้/ถอนได้ทันที. ไม่ต้องเขียนโค้ดเพิ่ม
html
<!-- ปุ่มลอยมาเองอยู่แล้ว — ปิดเฉพาะ app นี้ด้วย data-fab="off" -->
<script src="https://pdpa.web.rmutsv.ac.th/widget/v1/loader.js" data-app-key="..." data-fab="off" ...></script>

<!-- หรือทำปุ่มเองในตำแหน่งที่ต้องการ -->
<button onclick="window.pdpa.open()">จัดการคุกกี้</button>
กด “ปฏิเสธทั้งหมด” ใน banner = ถอน (WITHDRAW). ระบบเขียน record ใหม่เสมอ (append-only) ไม่ลบของเก่า → audit trail ครบ
แก้ปัญหาที่พบบ่อย

banner ไม่ขึ้น + /api/v1/policy ตอบ 404

ยังไม่ได้ publish policy (draft ไม่นับ) → ไปที่ app → policy → publish

CORS error ตอนเรียก /api/v1/consent

origin ของระบบไม่อยู่ใน allow-list → เพิ่ม origin (dev + prod) ที่หน้า app detail

POST /consent ตอบ 401 invalid subject proof

sig ผิด หรือ ts เก่าเกิน 10 นาที (clock เพี้ยน) หรือ subjectSecret ไม่ตรงกับที่ register

env เป็น undefined / script ไม่โผล่

framework อ่าน .env ตอน start เท่านั้น → restart dev server หลังแก้ env

กลัว secret หลุด

subjectSecret ใช้ฝั่ง server เท่านั้น — ห้าม prefix NEXT_PUBLIC_ / ห้ามใส่ใน bundle ของ SPA