Last active
December 3, 2025 09:12
-
-
Save PamornT/c8e334db7d447473584de16ac3041a5b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| const { onRequest } = require("firebase-functions/v2/https"); | |
| const fetch = require("node-fetch"); | |
| /** | |
| * ENV (ควรย้ายไป .env หรือ Firebase Config) | |
| */ | |
| const CLIENT_ID = "xxxxx"; // ใส่ Channel ID | |
| const CLIENT_SECRET = "xxxxx"; // ใส่ Channel Secret | |
| /** | |
| * 1. Get Stateless Token | |
| */ | |
| async function getStatelessToken() { | |
| const params = new URLSearchParams({ | |
| grant_type: "client_credentials", | |
| client_id: CLIENT_ID, | |
| client_secret: CLIENT_SECRET, | |
| }); | |
| const res = await fetch("https://api.line.me/oauth2/v3/token", { | |
| method: "POST", | |
| headers: { "Content-Type": "application/x-www-form-urlencoded" }, | |
| body: params, | |
| }); | |
| if (!res.ok) throw new Error("Failed to get stateless token"); | |
| const data = await res.json(); | |
| return data.access_token; | |
| } | |
| /** | |
| * 2. Get Notifier Token | |
| */ | |
| async function getNotifierToken(statelessToken, liffAccessToken) { | |
| const res = await fetch( | |
| "https://api.line.me/message/v3/notifier/token", | |
| { | |
| method: "POST", | |
| headers: { | |
| "Content-Type": "application/json", | |
| Authorization: `Bearer ${statelessToken}`, | |
| }, | |
| body: JSON.stringify({ | |
| liffAccessToken: liffAccessToken, | |
| }), | |
| } | |
| ); | |
| if (!res.ok) throw new Error("Failed to get notifier token"); | |
| const data = await res.json(); | |
| return data.notificationToken; | |
| } | |
| /** | |
| * 3. Send Service Message | |
| */ | |
| async function sendServiceMessage(statelessToken, notifierToken) { | |
| const res = await fetch( | |
| "https://api.line.me/message/v3/notifier/send?target=service", | |
| { | |
| method: "POST", | |
| headers: { | |
| "Content-Type": "application/json", | |
| Authorization: `Bearer ${statelessToken}`, | |
| }, | |
| body: JSON.stringify({ | |
| templateName: "join_d_m_en", | |
| params: { | |
| entry_date: "4 March 2025 09:41", | |
| btn1_url: "https://line.me", | |
| }, | |
| notificationToken: notifierToken, | |
| }), | |
| } | |
| ); | |
| if (!res.ok) throw new Error("Failed to send service message"); | |
| return await res.json(); | |
| } | |
| /** | |
| * 🔥 MAIN API | |
| * POST /sendServiceMessage | |
| * body: { liffAccessToken } | |
| */ | |
| exports.sendServiceMessage = onRequest(async (req, res) => { | |
| try { | |
| const { liffAccessToken } = req.body; | |
| if (!liffAccessToken) { | |
| return res.status(400).json({ error: "Missing liffAccessToken" }); | |
| } | |
| // Step 1 | |
| const statelessToken = await getStatelessToken(); | |
| // Step 2 | |
| const notifierToken = await getNotifierToken( | |
| statelessToken, | |
| liffAccessToken | |
| ); | |
| // Step 3 | |
| const result = await sendServiceMessage( | |
| statelessToken, | |
| notifierToken | |
| ); | |
| res.json({ | |
| success: true, | |
| statelessToken, | |
| notifierToken, | |
| result, | |
| }); | |
| } catch (err) { | |
| res.status(500).json({ | |
| error: err.message || "Internal Error", | |
| }); | |
| } | |
| }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment