สอนวิธีทำ Facebook Webhook step by step

บทความนี้จะพาไปทำความรู้จักเจ้า Facebook webhook ซึ่งเอาไปใช้ประโยชน์ได้หลายแบบโดยอธิบายง่ายๆคือ เจ้า Facebook Webhook คือการทำงานแบบ Real time event ต่างๆ เช่น ถ้ามีคนมา เขียน timeline ของ page หรือ comment หรือกด like ก็ตามจะส่งเหตุการณ์นั้นๆ มาให้ทาง url ที่เราตั้งไว้ ไปดูกันว่ามันทำยังไง

New update

ล่าสุดผมพบว่าการทำแบบนี้ยังมี Bug ที่ทาง Facebook ไม่แก้ไขคือ เมื่อเจ้าของ Page โพสภาพใน Timeline ตัวเองระบบจะไม่ส่ง callback มาเพราะฉะนั้นใครที่จะทำอย่าลืมบอกเจ้านายหรือเตือนไว้ก่อนด้วยว่าตรงนี้ยังไม่รองรับนะครับ สามารถติดตามเจ้า Bug ตัวนี้ได้จากทางนี้เลย

https://developers.facebook.com/bugs/1740620369486641/

ทำความเข้าใจก่อน

พระเอกของบทความนี้คือ Webhook ถูกต้องไหมครับ แต่ !!! การที่จะทำให้ webhook ทำงานนั้นจะต้องทำการ subscribed_apps แล้วเจ้า subscribed_apps คืออะไร ? คือ การที่เราจะอนุญาตให้ app ใดๆมาทำการติดตาม page เรา และเมื่อมีคน comment มันจะส่งว่ามี app ใน subscribed หรือเปล่าถ้ามีมันจะส่งให้ตัวอย่างเช่น

Page A มี App A ทำการติดตาม ( subscribed_apps ) อยู่ แต่ Page B ไม่มี ถ้าผมไป comment ใน Page A มันจะทำการดูว่ามี App ไหน subscribed ไว้หรือไม่ ปรากฎว่ามี App A ตามอยู่มันจะส่งค่าไปหา App A ว่ามีคน comment นะ ส่วน App A จะเอาค่าที่คนเขียน comment ไปทำอะไรต่อก็เรื่องของ App A ส่วนถ้าผมไป comment page B ก็ไม่มีอะไรขึ้นเกิดขึ้น

ฟังดูเหมือนก็ไม่มีอะไรนิเพราะถ้าเราทำ App Subscribtions ได้ก็จบแหละ ปัญหาอยู่ตรงนี้ครับ การจะ subscribe ได้นั้นมันต้องการ page_access_token แล้วเจ้า page_access_token ได้จากไหน มันได้จากเราต้องเอา user_access_token ไปแลกอีกที เห็นความยุ่งยากไหมครับ ? คราวนี้ขั้นตอนจะเป็นอย่างนี้ครับ

user -> คลิก login app เรา -> facebook gen user_access_token ให้ -> เอา user_access_token ไปแลกเป็น page_access_token -> เอา page_access_token ไป subscribe app

โครตจุกจิก -_-” กว่าผมจะเข้าใจก็เสียเวลาพอดูเลย

สิ่งที่ต้องเตรียมพร้อม

  1. ต้องมี url แบบ https บังคับต้องมี
  2. ไปสร้าง app สำหรับจัดการรับค่าจาก page เมื่อมีคนกระทำใดๆกับ page ของเรา
  3. account ที่เป็นเจ้าของ page ที่เราต้องการให้มัน webhook
  4. โหลด SDK ที่ต้องการของผมในบทความนี้ใช้ PHP SDK นะครับ https://developers.facebook.com/docs/php/gettingstarted

โอเคแหละจากเตรียมพร้อมหมดแล้วเราไปเริ่มกันว่าต้องทำอะไรก่อนหลังบ้าง ไปเริ่มที่ส่วนของ App ก่อนครับ

Set up App Webhook

บทความนี้จะไม่สอนทำ app นะครับจะสอนแค่ว่า set up ตรงไหนอย่างไรนะครับ เข้าไปที่ setting ของ app ตรงเมนูด้านซ้ายมือเลือก “+ Add Product”

facebook webhook

ในรูปคือผมสร้างแล้วนะครับ ถ้าเข้าไปครั้งแรกจะไม่เห็น Webhooks นะครับ เข้า Add Product เลือก Webhooks คราวนี้มันจะให้เลือกอะไรเยอะเยะ ในส่วน Field เลือก Feed โดยรายละเอียดว่าทำไมถึงเลือก Feed อ่านได้ตรงนี้ครับ

https://developers.facebook.com/docs/graph-api/webhooks

มันมีให้เลือกเลยว่าเราจะให้ Facebook เรียก url ที่เราตั้งไว้ เมื่อเกิด Event ไหน ในส่วนของ Feed จะรวมถึง comment , like , share , timeline พวกเกี่ยวกับ Feed ซึ่งถ้าหากคุณอยากทำพวก message แบบ Bot auto ก็สามารถทำได้เช่นกันคะรับ

โดยตอน set up นี้ตรงช่อง url นั้นเราต้องทำการ ยืนยันให้กับทาง Facebook โดย

facebook webhook

ตรงช่อง Verify Token คุณจะใส่อะไรก็ได้ครับตอนแรกผมก็งงเอามาจากไหนฟ่ะ แต่จริงๆคือคุณก็ gen รหัสอะไรซักอย่างก็ได้ครับ แล้วไปใส่ใน url ช่องแรกที่ให้ Facebook ลองเรียก การทำงานคือ Facebook จะลองเรียก url ที่เรากรอกแบบ Get request ซึ่งต้องเป็น HTTPS แหละต้องตอบกลับมาให้ทาง Facebook รู้ว่า url ที่กรอกเป็นของจริงไม่มั่ว โดยโค้ดในหน้าที่เรียกตัวอย่างจะทำนองนี้ครับ

สมมติว่าช่อง url: ผมกรอกไปว่า https://www.example.com/verifywebhook.php ในตัวไฟล์ verifywebhook.php code จะเป็นอย่างนี้ครับ

<?php

$challenge = $_REQUEST['hub_challenge'];
$verify_token = $_REQUEST['hub_verify_token'];

if ($verify_token === 'abc123') {
  echo $challenge;
}

$input = json_decode(file_get_contents('php://input'), true);
error_log(print_r($input, true));

ตรง abc123 ให้เราเอาค่าที่เรากรอกในช่อง Verify Token ครับ ถ้าคุณทำถูก Facebook จะอนุญาตให้คุณสร้าง subscribe ได้ครับโอเคครับเสร็จมีอีกจุดหนึ่งที่เราต้อง set up คือ url callback สำหรับการเอา token ครับ token อะไรยังไงเดี๋ยวอธิบายต่อนะครับ

Setting url callback

ยังอยู่ในเรื่องของ app นะครับเข้าเมนู setting เลื่อนลงมาด้านล่างแล้วกด + platform เลือก web แล้วก็ใส่ url สำหรับการ callback เวลาทำการ Login เพื่อขออนุญาตเข้าถึง page ต่างๆจะได้ redirect กลับมาที่ url นี้ครับ โอเคคราวนี้ก็จบตรง set up app แหละไปดูเรื่อง php กันครับ

Backend – php

ต่อไปเราจะทำหน้าสำหรับให้ user กดปุ่มแล้วทำการ redirect ไปขออนุญาตการเข้าถึง page ที่ user นั้นๆเป็นเจ้าของ ไฟล์ login.php จะมี code อย่างนี้ครับ

session_start();
$fb = new Facebook\Facebook([
  'app_id' => '{app-id}', // Replace {app-id} with your app id
  'app_secret' => '{app-secret}',
  'default_graph_version' => 'v2.2',
  ]);

$helper = $fb->getRedirectLoginHelper();

$permissions = ['manage_pages', 'pages_show_list']; // ตรงนี้แล้วแต่จะใส่ครับ
$loginUrl = $helper->getLoginUrl('https://example.com/fb-callback.php', $permissions);

echo '<a href="' . htmlspecialchars($loginUrl) . '">Log in with Facebook!</a>';

ต้อง include php sdk มาก่อนด้วยนะครับอย่าลืมผมไม่ได้เขียนไว้นะครับ ตรง $permission สามารถอ่านได้จากตรงนี้ครับ หลักๆคือต้องมี manage_pages นะครับ อ่านได้ที่นี่ https://developers.facebook.com/docs/pages/access-tokens

เสร็จเมื่อกดถ้าคุณเป็นคนทดสอบเองจะสามารถผ่านได้ด้วยดีแต่ถ้าหากสร้างมาทดสอบแล้วจะให้เพื่อนกด หรือเจ้าของ page มาทดสอบ app ของคุณต้องให้ คนที่คุณจะให้ทดสอบแอด account เข้าไปที่ app ด้วยนะครับ ตรงเมนู Role ลองหาดูครับถ้าไม่เจอจริงๆ comment ไว้ใต้บทความนะครับ

เสร็จเมื่อกด ok อนุญาตจากโค้ดหน้าบน ระบบจะ Redirect กลับมาที่ https://example.com/fb-callback.php ในหน้า fb-callback.php code จะเป็นอย่างนี้ครับ

session_start();
$fb = new Facebook\Facebook([
  'app_id' => '{app-id}', // Replace {app-id} with your app id
  'app_secret' => '{app-secret}',
  'default_graph_version' => 'v2.2',
  ]);

$helper = $fb->getRedirectLoginHelper();

try {
  $accessToken = $helper->getAccessToken();
} catch(Facebook\Exceptions\FacebookResponseException $e) {
  // When Graph returns an error
  echo 'Graph returned an error: ' . $e->getMessage();
  exit;
} catch(Facebook\Exceptions\FacebookSDKException $e) {
  // When validation fails or other local issues
  echo 'Facebook SDK returned an error: ' . $e->getMessage();
  exit;
}

if (! isset($accessToken)) {
  if ($helper->getError()) {
    header('HTTP/1.0 401 Unauthorized');
    echo "Error: " . $helper->getError() . "\n";
    echo "Error Code: " . $helper->getErrorCode() . "\n";
    echo "Error Reason: " . $helper->getErrorReason() . "\n";
    echo "Error Description: " . $helper->getErrorDescription() . "\n";
  } else {
    header('HTTP/1.0 400 Bad Request');
    echo 'Bad request';
  }
  exit;
}

// Logged in
echo '<h3>Access Token</h3>';
var_dump($accessToken->getValue());

// The OAuth 2.0 client handler helps us manage access tokens
$oAuth2Client = $fb->getOAuth2Client();

// Get the access token metadata from /debug_token
$tokenMetadata = $oAuth2Client->debugToken($accessToken);
echo '<h3>Metadata</h3>';
var_dump($tokenMetadata);

// Validation (these will throw FacebookSDKException's when they fail)
$tokenMetadata->validateAppId({app-id}); // Replace {app-id} with your app id
// If you know the user ID this access token belongs to, you can validate it here
//$tokenMetadata->validateUserId('123');
$tokenMetadata->validateExpiration();

if (! $accessToken->isLongLived()) {
  // Exchanges a short-lived access token for a long-lived one
  try {
    $accessToken = $oAuth2Client->getLongLivedAccessToken($accessToken);
  } catch (Facebook\Exceptions\FacebookSDKException $e) {
    echo "<p>Error getting long-lived access token: " . $helper->getMessage() . "</p>\n\n";
    exit;
  }

  echo '<h3>Long-lived</h3>';
  var_dump($accessToken->getValue());
}

$_SESSION['fb_access_token'] = (string) $accessToken;

// User is logged in with a long-lived access token.
// You can redirect them to a members-only page.
//header('Location: https://example.com/members.php');

สิ่งสำคัญของไฟล์ทั้งสองคือต้องอย่าลืมใส่ session_start(); ด้วยนะครับไม่เข้าใจเหมือนกันว่าทำไม Facebook มันไม่เขียนลงไปด้วย มาเขียนบอกทีหลัง ผมใส่ให้แหละใน code ตัวอย่างทั้งสอง อย่าลืมไปแก้ไข app_id, app_secret_id นะครับ

จากโค้ดด้านบนถ้าทำสำเร็จไม่ติดอะไร ระบบที่เราเขียนจะได้ user_access_token มาแหละ ที่เหลือก็คือเอาไปแลกต่อเป็น page_access_token โดยคุณอาจจะสร้างไฟล์ใหม่หรือสามารถเอาโค้ดมาต่อทำที่เดียวได้เลย ในตัวอย่างผมจะแยกก่อนแล้วกันครับ สร้างไฟล์อีกอันหนึ่งชื่อ get_page_access_token.php code เป็นอย่างนี้ครับ

<?php
session_start();
$fb = new Facebook\Facebook([
  'app_id' => '{app-id}', // Replace {app-id} with your app id
  'app_secret' => '{app-secret}',
  'default_graph_version' => 'v2.2',
  'default_access_token' => $_SESSION['fb_access_token']
  ]);

$response = $fb->get('/{page_id}/field=access_token');

$content = json_decode($response->getBody(), true);

$_SESSION['page_access_token'] = $content['access_token'];


อย่าลืมแก้ page_id นะครับเป็น หมายเลข id ของ page ที่คุณต้องการจะติดตามโดยไปดูได้จากหน้า about ใน page ครับจะอยู่ด้านล่างสุด

ให้คุณลองเรียกหน้า get_page_accss_token.php ดูครับถ้าใน session มีตัวแปร fb_access_token อยู่จะสามารถเรียกได้ไม่มีปัญหา ส่วนทำไมต้องเขียน getBody เพราะค่าที่คืนกลับมามันจะเป็น json ครับไม่ใช่ access_token อย่างเดียวครับ สามารถอ่านได้จากที่นี่ครับ

https://developers.facebook.com/docs/php/Facebook/5.0.0

คราวนี้สุดท้ายแล้วครับ เมื่อเราได้ page_access_token มาแล้วก็เอาไป subscribed_apps ได้ซักที สร้างไฟล์ subscribed_app.php มาครับแล้ว ใส่ code ดังนี้

<?php
session_start();
$fb = new Facebook\Facebook([
  'app_id' => '{app-id}', // Replace {app-id} with your app id
  'app_secret' => '{app-secret}',
  'default_graph_version' => 'v2.2',
  'default_access_token' => $_SESSION['page_access_token']
  ]);

$response = $fb->post('/{page_id}/subscribed_apps');

$content = json_decode($response->getBody(), true);

echo $content['success'];

ถ้าไม่ผิดพลาดอะไรเราจะได้เลข 1 ทางหน้าจอครับ ต่อไปเราจะทำการเทสว่า Facebook จะเรียก callback กลับมาหาเราจริงหรือไม่ และให้อะไรมาโดยการกลับไปที่ไฟล์ verifywebhook.php ครับให้ทำการแก้ไขเป็นแบบนี้

<?php


$input = json_decode(file_get_contents('php://input'), true);

$fp = fopen('feed.txt', a);
fwrite($fp, print_r($input, true));
fclose($fp);

ถ้าไม่มีปัญหาอะไร เมื่อเราไปเขียน timeline,comment, etc. กับ page นั้นๆที่เราทำการขออนุญาตในตอนแรกแล้ว Facebook จะ callback มาที่ url ที่เราทำการสร้างไว้ครับ หน้าตาสิ่งที่ส่งมาจะเป็นอย่างนี้ครับ

Array
(
    [entry] => Array
        (
            [0] => Array
                (
                    [changes] => Array
                        (
                            [0] => Array
                                (
                                    [field] => feed
                                    [value] => Array
                                        (
                                            [parent_id] => 416789195042914_793208914067605
                                            [sender_name] => Karun Jaraslertsuwan
                                            [comment_id] => 793208914067605_1015940881794406
                                            [sender_id] => 1191924570832460
                                            [item] => comment
                                            [verb] => add
                                            [created_time] => 1468495872
                                            [post_id] => 416789195042914_793208914067605
                                            [message] => ภาพสวยจังเลย
                                        )

                                )

                        )

                    [id] => 416789195042914
                    [time] => 1468495873
                )

        )

    [object] => page
)
Loading

เป็นโปรแกรมเมอร์ที่ตามหาคุณค่าของชีวิตและความฝันในวัยเด็ก ชอบเล่นเกม เรียนรู้ทุกอย่าง ชอบเจอคนใหม่ๆ งานสังคมทุกชนิด ออกกำลังกายในวันว่าง อ่านหนังสือ มีเว็บรีวิวหนังสือด้วย www.readraide.in.th