Salesforce Open Badges: ออกข้อมูลรับรองที่ตรวจสอบได้จากทริกเกอร์ของ CRM

เริ่มออก Open Badge จากเหตุการณ์ใดก็ได้ของออบเจ็กต์ใน Salesforce เช่น Lead ปิดการขายได้ Account อัปเกรด หรือบรรลุเป้าหมายการอบรม ด้วย Apex callout ไปยัง Badges Ninja API

Nacho Coll โดย อัปเดตล่าสุด 4 นาทีอ่าน
เริ่มออก Open Badge จากเหตุการณ์ใดก็ได้ของออบเจ็กต์ใน Salesforce เช่น Lead ปิดการขายได้ Account อัปเกรด หรือบรรลุเป้าหมายการอบรม ด้วย Apex callout ไปยัง Badges Ninja API

ทีมฝึกอบรมส่วนใหญ่บันทึกใน Salesforce อยู่แล้วว่าใครเรียนจบอะไร การลงทะเบียนเรียนเปลี่ยนเป็น «Completed» บัญชีพาร์ตเนอร์ขึ้นสู่ระดับการรับรอง ลูกค้าทำออนบอร์ดดิงเสร็จ ข้อมูลในเรคคอร์ดเปลี่ยนไปแล้ว แต่หนึ่งสัปดาห์ต่อมาก็ยังต้องมีคนส่งออกรายชื่อแล้วอีเมลใบรับรองด้วยมือ

คู่มือนี้แสดงวิธีปิดช่องว่างนั้น เมื่อเรคคอร์ดถึงสถานะที่คุณต้องการ Salesforce จะเรียก API ของ https://badges.ninja จากนั้นระบบจะออกข้อมูลรับรอง Open Badge v2.0 และเก็บลิงก์ตรวจสอบกลับไว้ในเรคคอร์ด คุณนำลิงก์นั้นไปใส่ในเทมเพลตอีเมล รายงาน หรือหน้า Experience Cloud ได้ทันที

คุณต้องมีผู้ดูแลระบบ Salesforce (หรือ developer sandbox) ตราสัญลักษณ์ดิจิทัลที่ออกแบบไว้แล้วบน https://badges.ninja และคีย์ API หากคุณไม่เคยเรียก API มาก่อน ให้อ่านคู่มือเริ่มต้นใช้งาน API ก่อน ใช้เวลาแค่สิบนาที

เมื่อทริกเกอร์ของ CRM เป็นเครื่องมือที่ใช่

ก่อนเขียน Apex ให้ตรวจดูก่อนว่าคุณจำเป็นต้องใช้จริงหรือไม่ หากคุณออกข้อมูลรับรองเป็นชุดปีละหนึ่งภาคเรียนหรือไตรมาสละครั้ง การอัปโหลด CSV ในแดชบอร์ดจะเร็วกว่าและไม่ต้องเขียนโค้ดเลย เส้นทางผ่าน Salesforce คุ้มค่าเมื่อ:

  • ต้องการให้ออกทันทีที่เรคคอร์ดเปลี่ยน โดยไม่มีขั้นตอนที่คนต้องทำเอง
  • ข้อมูลว่า «ใครได้รับ» อยู่ใน Salesforce อยู่แล้ว และคุณไม่ต้องการแหล่งข้อมูลจริงที่สอง
  • ต้องการเก็บลิงก์ข้อมูลรับรองไว้ใน Contact, Lead หรือเรคคอร์ดอบรมแบบกำหนดเองเพื่อใช้ทำรายงาน

หากเครื่องมือเวิร์กโฟลว์ของคุณคือ Zapier ไม่ใช่ Apex การเรียก API แบบเดียวกันก็ใช้ได้ที่นั่นเช่นกัน ดูระบบอัตโนมัติ Open Badges ด้วย Zapier

สิ่งที่คุณกำลังสร้าง

ขั้นตอนทั้งหมดมีสี่ส่วน:

  1. ตราสัญลักษณ์ดิจิทัล ที่ออกแบบในตัวออกแบบแบบภาพ คัดลอก id ของตราสัญลักษณ์นั้น (ค่า id ที่ GET /badges ส่งกลับมา)
  2. คีย์ API ที่สร้างในหน้า API Keys ของแดชบอร์ด คีย์จะแสดงเพียงครั้งเดียว จึงควรเก็บไว้ทันที
  3. Named Credential ใน Salesforce ที่เก็บคีย์ไว้ เพื่อไม่ให้คีย์ปรากฏในโค้ด
  4. Apex trigger พร้อมคลาส Queueable ที่ออก award และเขียนลิงก์ award กลับลงเรคคอร์ด

Newly created API key revealed once

ขั้นตอนที่ 1: เก็บคีย์ไว้ใน Named Credential

การฝังคีย์ API ลงใน Apex โดยตรงเป็นข้อผิดพลาดที่ควรหลีกเลี่ยง ใน Setup ให้สร้าง External Credential ที่มี header กำหนดเอง แล้วสร้าง Named Credential ที่ชี้ไปยังโฮสต์ของ API:

  • URL: https://api.badges.ninja
  • ชื่อ header ที่กำหนดเอง: X-Api-Key
  • ค่า header ที่กำหนดเอง: คีย์ของคุณ ในรูปแบบ bws_YOUR_KEY_HERE
  • ชื่อ Named Credential: BadgesNinja

ให้ permission set ที่รันระบบอัตโนมัติของคุณเข้าถึง principal ของ External Credential ได้ เมื่อมี Named Credential แล้วคุณไม่ต้องตั้งค่า Remote Site Setting แยกต่างหาก เพราะ Salesforce อนุญาต callout ผ่านตัว credential หากองค์กรของคุณยังใช้ Named Credential แบบเดิมหรือ endpoint ธรรมดา ให้เพิ่ม https://api.badges.ninja เป็น Remote Site Setting แล้วส่ง header จากโค้ดแทน

ขั้นตอนที่ 2: เพิ่มฟิลด์ลงในเรคคอร์ด

ในออบเจ็กต์ที่แทนความสำเร็จ (เราจะใช้ออบเจ็กต์แบบกำหนดเองชื่อ Training_Enrollment__c) ให้เพิ่ม:

  • Badge_Id__c (Text): ตราสัญลักษณ์ที่จะออก การเก็บค่านี้ไว้ต่อหนึ่งหลักสูตรทำให้ทริกเกอร์ตัวเดียวรองรับตราสัญลักษณ์ได้หลายแบบ
  • Badge_Award_Id__c (Text, unique): id ของ award ที่ API ส่งกลับมา
  • Badge_Url__c (URL): หน้าตรวจสอบสาธารณะ
  • Badge_Error__c (Long Text): ข้อผิดพลาดล่าสุด เพื่อให้ผู้ดูแลระบบเห็นความล้มเหลวโดยไม่ต้องเปิด debug log

ฟิลด์ Badge_Award_Id__c ทำหน้าที่สองอย่าง คือเป็นตัวกันการออกซ้ำ ทริกเกอร์จะออกก็ต่อเมื่อฟิลด์นี้ว่างเท่านั้น

ขั้นตอนที่ 3: Apex callout

ทริกเกอร์เรียก callout โดยตรงไม่ได้ เพราะไม่อนุญาตให้ทำ callout ในทรานแซกชันเดียวกับ DML ที่ยังไม่ commit ให้ส่งงานต่อไปยัง Queueable ที่ implement Database.AllowsCallouts

สัญญา API ของ POST /awards สั้นมาก body ประกอบด้วย badgeId ออบเจ็กต์ซ้อน recipient ที่มี name และ email และ issuedOn ซึ่งจำเป็นต้องมี เมื่อเรียกสำเร็จจะได้ HTTP 200 พร้อม { "awardId": "..." } ชื่อผู้รับต้องมีอย่างน้อยห้าตัวอักษร ดังนั้น Contact ที่ชื่อ «Al Li» จะถูกปฏิเสธ ควรตรวจสอบข้อมูลก่อนเรียก

public class BadgeIssueJob implements Queueable, Database.AllowsCallouts {
    private Id enrollmentId;
    public BadgeIssueJob(Id enrollmentId) { this.enrollmentId = enrollmentId; }

    public void execute(QueueableContext ctx) {
        Training_Enrollment__c e = [
            SELECT Id, Badge_Id__c, Badge_Award_Id__c, Contact__r.Name, Contact__r.Email
            FROM Training_Enrollment__c WHERE Id = :enrollmentId LIMIT 1
        ];
        if (e.Badge_Award_Id__c != null) { return; }   // already issued

        Map<String, Object> recipient = new Map<String, Object>{
            'name' => e.Contact__r.Name,
            'email' => e.Contact__r.Email
        };
        Map<String, Object> body = new Map<String, Object>{
            'badgeId' => e.Badge_Id__c,
            'recipient' => recipient,
            'issuedOn' => Datetime.now().getTime()
        };

        HttpRequest req = new HttpRequest();
        req.setEndpoint('callout:BadgesNinja/awards');
        req.setMethod('POST');
        req.setHeader('Content-Type', 'application/json');
        req.setBody(JSON.serialize(body));

        HttpResponse res = new Http().send(req);
        if (res.getStatusCode() == 200) {
            Map<String, Object> out = (Map<String, Object>) JSON.deserializeUntyped(res.getBody());
            String awardId = (String) out.get('awardId');
            e.Badge_Award_Id__c = awardId;
            e.Badge_Url__c = 'https://badges.ninja/awards/' + awardId;
            e.Badge_Error__c = null;
        } else {
            e.Badge_Error__c = res.getStatusCode() + ': ' + res.getBody();
        }
        update e;
    }
}

มีสองจุดที่ควรสังเกต อย่างแรก issuedOn เป็นมิลลิวินาทีแบบ epoch ซึ่งตรงกับสิ่งที่ Datetime.getTime() ส่งกลับ อย่างที่สอง ผลลัพธ์ที่ได้ไม่มี URL ตรวจสอบ โค้ดจึงสร้างขึ้นจาก award id เอง: https://badges.ninja/awards/<awardId>

ขั้นตอนที่ 4: ทริกเกอร์

ทำให้ทริกเกอร์เบาที่สุด ให้มันตัดสินแค่ว่าเรคคอร์ดใดเข้าเกณฑ์ และเข้าคิวหนึ่งงานต่อหนึ่งเรคคอร์ด

trigger TrainingEnrollmentBadge on Training_Enrollment__c (after insert, after update) {
    for (Training_Enrollment__c e : Trigger.new) {
        Boolean completed = e.Status__c == 'Completed';
        Boolean wasCompleted = Trigger.isUpdate && Trigger.oldMap.get(e.Id).Status__c == 'Completed';
        if (completed && !wasCompleted && e.Badge_Award_Id__c == null && e.Badge_Id__c != null) {
            System.enqueueJob(new BadgeIssueJob(e.Id));
        }
    }
}

การตรวจ wasCompleted สำคัญมาก หากไม่มี การแก้ไขเรคคอร์ดที่เสร็จสมบูรณ์แล้วในภายหลังจะทำให้ตรรกะทำงานซ้ำ เมื่อรวมกับตัวกันที่ตรวจว่า award id ว่างภายในงาน เรคคอร์ดหนึ่งจะออกข้อมูลรับรองได้เพียงชุดเดียวเท่านั้น

ข้อจำกัดหนึ่งที่ต้องคำนึงถึง: Salesforce อนุญาตให้เข้าคิวงาน Queueable จากทรานแซกชันเดียวได้ไม่มาก (50 งานในบริบทแบบซิงโครนัส) หากคุณโหลดรายการที่เรียนจบแบบกลุ่ม เช่น นำเข้าข้อมูล 2,000 แถว ทริกเกอร์ข้างต้นจะชนเพดานนี้ สำหรับการโหลดแบบกลุ่ม ให้อัปโหลดรายชื่อผ่านการออก award แบบกลุ่มด้วย CSV ในแดชบอร์ดแทน หรือเปลี่ยนไปใช้คลาส Batchable ที่มี Database.AllowsCallouts ซึ่งประมวลผลทีละไม่กี่เรคคอร์ดต่อ scope

API keys list showing the masked prefix

การจัดการข้อผิดพลาดและการลองใหม่

API ส่งรหัสสถานะมาตรฐานกลับมา และแต่ละรหัสต้องการการตอบสนองที่ต่างกันจากโค้ดของคุณ:

สถานะความหมายสิ่งที่ควรทำ
400ฟิลด์หายไปหรือไม่ถูกต้อง รวมถึงชื่อผู้รับที่สั้นกว่าห้าตัวอักษรอย่าลองใหม่ แก้ข้อมูลแล้วเขียนข้อความลงใน Badge_Error__c
401คีย์ API ผิดหรือไม่มีตรวจสอบ Named Credential การลองใหม่จะไม่ช่วยจนกว่าจะแก้ไข
402ข้อมูลรับรองหรือโควตาของแผนหมดหยุดออกและแจ้งผู้ดูแลระบบ ลองใหม่หลังอัปเกรดหรือหลังโควตารีเซ็ต
404ไม่มี badgeId นี้อยู่แก้ Badge_Id__c ในเรคคอร์ดหรือหลักสูตร
5xx หรือหมดเวลาข้อขัดข้องชั่วคราวลองใหม่ภายหลังได้อย่างปลอดภัย

เนื่องจากงานจะเขียนข้อผิดพลาดลงเรคคอร์ดและปล่อย Badge_Award_Id__c ให้ว่าง การลองใหม่จึงง่ายเพียงแค่ติ๊กเช็กบ็อกซ์หรือบันทึกเรคคอร์ดอีกครั้ง คลาส Apex แบบตั้งเวลาขนาดเล็กที่ค้นหาการลงทะเบียนที่เสร็จสมบูรณ์แต่ไม่มี award id และมีข้อผิดพลาดชั่วคราว แล้วนำกลับเข้าคิวใหม่ จะให้คิวลองใหม่อัตโนมัติโดยไม่ต้องมีโครงสร้างพื้นฐานเพิ่ม ควรจำกัดจำนวนครั้งด้วยฟิลด์ตัวนับ เพื่อไม่ให้เรคคอร์ดที่เสียถาวรวนซ้ำไม่รู้จบ

ตรวจสอบแผนของคุณก่อนเชื่อมต่อกับระบบจริง แผน Free ครอบคลุมข้อมูลรับรอง 1,200 รายการต่อปี Starter ราคา $9 ต่อเดือน และ Pro ราคา $29 ต่อเดือน ทริกเกอร์ที่ทำงานกับทุก Lead จะใช้แผนเล็กหมดอย่างรวดเร็ว จึงควรจำกัดเกณฑ์ไว้ที่ความสำเร็จจริง

ทำให้ข้อมูลรับรองมองเห็นได้ใน Salesforce

เมื่อ Badge_Url__c ถูกเติมแล้ว คุณนำไปใช้ได้ทุกที่:

  • การแจ้งเตือนทางอีเมลและเทมเพลต: ผสานฟิลด์ลงในอีเมล «ขอแสดงความยินดี นี่คือข้อมูลรับรองของคุณ»
  • รายงาน: รายงานที่กรอง Badge_Award_Id__c != null ให้จำนวนที่ออกแยกตามหลักสูตร ภูมิภาค หรือเจ้าของบัญชี
  • Experience Cloud: แสดงลิงก์ในหน้าการรับรองของพาร์ตเนอร์ เพื่อให้ผู้ซื้อตรวจสอบได้เอง
  • เลย์เอาต์หน้า: เพิ่มฟิลด์ URL เพื่อให้ทีมซัพพอร์ตตอบคำถามว่า «เธอได้รับตราสัญลักษณ์แล้วหรือยัง» ได้ในพริบตา

หน้าตรวจสอบเป็นสาธารณะและไม่ต้องสมัครสมาชิก ซึ่งเป็นจุดประสงค์หลักของมัน ผู้รับ ผู้จัดการฝ่ายสรรหาบุคลากร หรือฝ่ายจัดซื้อของลูกค้าเปิดลิงก์แล้วยืนยันได้ว่าใครเป็นผู้ออกข้อมูลรับรอง รับรองอะไร และเมื่อใด สำหรับการตรวจสอบด้วยเครื่อง JSON-LD ของ Open Badge v2.0 ของ award ใดก็ได้อยู่ที่ https://api.badges.ninja/certify-badge/award/<awardId>

ผู้รับยังได้ของแถมตามปกติ ได้แก่ URL ตรวจสอบเฉพาะตัว คิวอาร์โค้ด ใบรับรอง PDF ขนาด A4 และตัวเลือก Add to LinkedIn จากพอร์ทัลผู้รับ ซึ่งลงชื่อเข้าใช้ด้วย magic link แทนรหัสผ่าน

ทำไมวิธีนี้ดีกว่าปลั๊กอินตราสัญลักษณ์บน AppExchange

ปลั๊กอินตราสัญลักษณ์บน AppExchange มักคิดราคาตามจำนวนที่นั่ง Salesforce หรือตามไลเซนส์ของ managed package ต้นทุนนั้นเพิ่มตามขนาดทีม Salesforce ของคุณ ไม่ใช่ตามจำนวนข้อมูลรับรองที่คุณออก ทีมอบรมสิบคนกับโปรแกรมที่มีผู้เรียนหนึ่งหมื่นคนจึงจ่ายต่างกันมากสำหรับผลลัพธ์เดียวกัน

การเชื่อมต่อผ่าน API พลิกโมเดลนั้น คุณจ่ายตามปริมาณข้อมูลรับรองด้วยแผนราคาคงที่ โค้ดทริกเกอร์เป็นของคุณที่อ่านและแก้ไขได้ และผลลัพธ์เป็น Open Badge v2.0 มาตรฐาน ผู้รับจึงไม่ถูกผูกกับตัวแสดงผลเฉพาะของใคร คุณยังมีอิสระในการออกแบบ ตัวออกแบบแบบภาพมีเทมเพลต รูปทรง ไอคอน และการอัปโหลดแบบกำหนดเองกว่า 80 แบบ แทนที่จะเป็นเลย์เอาต์ตายตัวภายในแพ็กเกจ

ข้อแลกเปลี่ยนที่ต้องยอมรับตามจริงคือ ปลั๊กอินติดตั้งได้ในคลิกเดียวและไม่ต้องใช้ Apex หากไม่มีใครในทีมดูแลโค้ดไม่กี่สิบบรรทัดได้ ปลั๊กอินหรือตัวเชื่อมต่อแบบ no-code อาจเหมาะกว่า หากคุณกำลังเปรียบเทียบแพลตฟอร์มโดยรวม ไม่ใช่การเชื่อมต่อ บทวิเคราะห์ทางเลือกแทน Credly เทียบราคาและฟีเจอร์ไว้เคียงข้างกัน

ทดสอบก่อนใช้งานจริง

รันขั้นตอนทั้งหมดใน sandbox ก่อน

  1. สร้างตราสัญลักษณ์ทดสอบและผู้รับทดสอบที่ใช้อีเมลที่คุณควบคุมได้
  2. ในการทดสอบ Apex ให้ implement HttpCalloutMock ที่ส่งสถานะ 200 และ {"awardId":"test-award-id"} กลับมา Salesforce ไม่อนุญาต callout จริงในบริบทการทดสอบ และคุณต้องมี coverage นั้นเพื่อ deploy
  3. ใน sandbox ตั้งการลงทะเบียนหนึ่งรายการเป็น Completed แล้วตรวจว่า Badge_Award_Id__c และ Badge_Url__c ถูกเติมภายในหนึ่งนาที
  4. เปิด URL ในหน้าต่างส่วนตัวของเบราว์เซอร์ เพื่อยืนยันว่าหน้าตรวจสอบสาธารณะแสดงผลได้โดยไม่ต้องลงชื่อเข้าใช้
  5. บังคับให้เกิดข้อผิดพลาด (ลบ badge id ให้ว่าง หรือใช้ชื่อผู้รับที่สั้นกว่าห้าตัวอักษร) แล้วยืนยันว่าข้อผิดพลาดไปอยู่ใน Badge_Error__c

หมุนเวียนคีย์ API หากคีย์รั่วไหลลงใน debug log หรือภาพหน้าจอ การเพิกถอนมีผลทันทีจากหน้า API Keys และคุณเพียงอัปเดต External Credential ไม่ต้องแก้โค้ด

สรุป

รูปแบบนี้เล็กนิดเดียว ทริกเกอร์ที่เบาตัดสินว่าเมื่อไร Queueable ทำ callout Named Credential เก็บคีย์ และสามฟิลด์ในเรคคอร์ดเก็บผลลัพธ์ ใช้ได้กับเป้าหมายการอบรม ระดับพาร์ตเนอร์ การเข้าร่วมงานอีเวนต์ หรือการเปลี่ยนสถานะใดก็ตามที่คุณแสดงใน Salesforce ได้

เริ่มจากหนึ่งออบเจ็กต์กับหนึ่งตราสัญลักษณ์ ดูผลสักหนึ่งสัปดาห์ แล้วขยายเมื่อพฤติกรรมการลองใหม่เหมาะกับข้อมูลของคุณ

พร้อมออกข้อมูลรับรองที่ตรวจสอบได้ชุดแรกของคุณหรือยัง เริ่มใช้ฟรีที่ badges.ninja — ตัวออกแบบแบบภาพ หน้าตรวจสอบสาธารณะ ใบรับรอง PDF และผลลัพธ์ Open Badge v2.0 ไม่ต้องใช้บัตรเครดิต

Nacho Coll

เกี่ยวกับผู้เขียน

Founder & Engineer at Badges Ninja

Nacho founded Badges Ninja to make issuing verifiable digital credentials as simple as a single API call — Open Badge v2.0 badges and certificates, minted, hosted, and verifiable without standing up your own issuer infrastructure. Writes about the Open Badges spec, credential verification, and running a credentialing platform serverless on AWS, from the operator side of the wire.

กลับไปที่บล็อก

บทความที่เกี่ยวข้อง