เริ่มต้นใช้งาน

SDK ของไลบรารีการเข้าถึงแบบเป็นโปรแกรม (PAL) สำหรับ Roku ช่วยให้ผู้เผยแพร่โฆษณาสามารถอนุมัติการโทรผ่าน VAST (DVC) โดยตรงเพื่อสร้างรายได้จากแอปพลิเคชัน Roku ที่ใช้ DVC PAL SDK อนุญาตให้คุณส่งคำขอ Nonce ซึ่งเป็นสตริงที่เข้ารหัสจาก Google เพื่อให้คุณลงนามในคำขอ DVC ได้ คำขอสตรีมใหม่แต่ละรายการต้องมาพร้อมกับ Nonce ที่สร้างขึ้นใหม่ อย่างไรก็ตาม คุณสามารถใช้ค่าที่ได้จากการส่งคำขอโฆษณาเดียวกันซ้ำสำหรับ คำขอโฆษณาหลายรายการภายในสตรีมเดียวกันได้

คู่มือนี้จะอธิบายตัวอย่างวิธีรวม PAL SDK ลงในแอปพลิเคชัน Roku, ขอ Nonce และการลงทะเบียนการแสดงโฆษณา

ข้อกำหนดเบื้องต้น

ก่อนที่จะเริ่มคู่มือนี้ คุณต้องมีคุณสมบัติต่อไปนี้

  • สภาพแวดล้อมในการพัฒนาซอฟต์แวร์ Roku - ดูข้อมูลเพิ่มเติมในคู่มือการตั้งค่าสภาพแวดล้อมของ Roku Developer
  • โฟลเดอร์โปรเจ็กต์ที่มีโครงสร้างต่อไปนี้

    ./
      components/
        MainScene.xml
        PALInterface.xml
        SampleVideoPlayer.xml
      images/
        icon_focus_hd.png
        icon_focus_sd.png
        icon_side_hd.png
        icon_side_sd.png
        splash_fhd.png
        splash_hd.png
        splash_sd.png
      source/
        main.brs
      manifest
    

ตั้งค่าโปรเจ็กต์

คุณต้องกำหนดค่าไฟล์โปรเจ็กต์ก่อนที่จะผสานรวม PAL SDK

ไฟล์ Manifest

title=PAL for Roku Sample
subtitle=As seen in the PAL for Roku Get Started Guide
major_version=1
minor_version=0
build_version=00001

mm_icon_focus_hd=pkg:/images/icon_focus_hd.png
mm_icon_side_hd=pkg:/images/icon_side_hd.png
mm_icon_focus_sd=pkg:/images/icon_focus_sd.png
mm_icon_side_sd=pkg:/images/icon_side_sd.png

splash_screen_sd=pkg:/images/splash_sd.jpg
splash_screen_hd=pkg:/images/splash_hd.jpg
splash_screen_fhd=pkg:/images/splash_fhd.jpg
splash_color=#000000
splash_min_time=1000
ui_resolutions=hd

แหล่งที่มา/main.brs

sub Main()
    showChannelSGScreen()
end sub

sub showChannelSGScreen()
  screen = CreateObject("roSGScreen")
  m.port = CreateObject("roMessagePort")

  screen.setMessagePort(m.port)
  m.scene = screen.CreateScene("MainScene")
  screen.show()

  while(true)
    msg = wait(0, m.port)
    msgType = type(msg)
    if msgType = "roSGScreenEvent"
      if msg.isScreenClosed() then return
    end if
  end while
end sub

สร้างวิดีโอเพลเยอร์ตัวอย่าง

คอมโพเนนต์ SampleVideoPlayer จะรวมคอมโพเนนต์วิดีโอเพื่อจับภาพการกดรีโมตคอนโทรล ลบล้าง onKeyEvent เพื่อให้เมื่อโอนโฟกัสของรีโมตไปยังวิดีโอ/โปรแกรมเล่นโฆษณาแล้ว การกดแป้นเพิ่มเติม (ขึ้น ลง ซ้าย ขวา คลิก ฯลฯ) ถูกจับและแสดงเป็นบับเบิลถึง PAL

components/SampleVideoPlayer.xml

<?xml version="1.0" encoding="utf-8" ?>

<component name="SampleVideoPlayer" extends="Video">
  <interface>
    <field id="pressedKey" type="String" />
  </interface>
  <script type="text/brightscript">
    <![CDATA[

      Function onKeyEvent(key as String, press as Boolean) as Boolean
        If press
          m.top.pressedKey = key
        End If
        return True
      End Function

    ]]>
  </script>

  <children>
    <Label text="VIDEO" color="0xFFFFFFFF" font="font:MediumBoldSystemFont" horizAlign="center" vertAlign="center" width="720" height="480" />
  </children>

</component>

สร้างอินเทอร์เฟซทดสอบ

ใช้ฉากที่มีปุ่มเพื่อทำสิ่งต่อไปนี้

  • ขอค่าที่ได้จากการสุ่ม
  • ส่งการคลิกโฆษณา
  • ส่งเหตุการณ์ที่เริ่มเล่นแล้ว
  • ส่งเหตุการณ์ที่สิ้นสุดการเล่น
  • โอนโฟกัสไปที่ปุ่มวิดีโอ

components/MainScene.xml

<?xml version="1.0" encoding="utf-8" ?>
<component name="MainScene" extends="Scene" initialFocus="requestNonceButton">
<children>
  <ButtonGroup>
    <button text="Request Nonce" id="requestNonceButton" />
    <button text="Send Ad Click" id="sendAdClickButton" />
    <button text="Send Playback Start" id="sendPlaybackStartButton" />
    <button text="Send Playback End" id="sendPlaybackEndButton" />
    <button text="Transfer Focus to Video" id="transferFocusToVideoButton" />
  </ButtonGroup>
  <SampleVideoPlayer id="YourVideoPlayer" width="720" height="480" focusable="true" />
</children>
</component>

สร้างคอมโพเนนต์อินเทอร์เฟซ SDK

หากต้องการสื่อสารระหว่างฉากหลักและ PAL SDK คุณต้องมีคอมโพเนนต์ที่มีโค้ดแบบอะซิงโครนัส ซึ่งเป็นสิ่งจำเป็นเนื่องจาก PAL SDK สร้างคำขอเครือข่ายภายนอก ซึ่งไม่สามารถเกิดขึ้นบนเทรดหลักในแอปพลิเคชัน Roku ในการส่งข้อมูลไปยังคอมโพเนนต์นี้ คุณต้องมีอินเทอร์เฟซที่กำหนดข้อมูลที่คอมโพเนนต์จะส่งและรับ

components/PALInterface.xml

<?xml version="1.0" encoding="utf-8" ?>
<component name="PALInterface" extends="Task">
<interface>
  <!--Commands-->
  <field id="requestNonce" type="Boolean" />
  <field id="sendAdClick" type="Boolean" />
  <field id="sendAdTouchKey" type="String" />
  <field id="sendPlaybackStart" type="Boolean" />
  <field id="sendPlaybackEnd" type="Boolean" />
  <field id="endThread" type="Boolean" />
  <!--Responses-->
  <field id="errors" type="stringarray" />
  <field id="nonce" type="String" />
</interface>
</component>

นำเข้า IMA SDK

หากต้องการใช้ไลบรารี PAL คุณต้องมี IMA SDK สำหรับ Roku ในไฟล์ Manifest ของแอปและนำเข้าไปยังคอมโพเนนต์ PALInterface

ไฟล์ Manifest

...
splash_color=#000000
splash_min_time=1000
ui_resolutions=hd
bs_libs_required=googleima3

components/PALInterface.xml

<?xml version = "1.0" encoding = "utf-8" ?>

<component name="PALInterface" extends="Task">
<interface>
  <!-- commands -->
  <field id="requestNonce" type="Boolean" />
  <field id="sendAdClick" type="Boolean" />
  <field id="sendAdTouchKey" type="String" />
  <field id="sendPlaybackStart" type="Boolean" />
  <field id="sendPlaybackEnd" type="Boolean" />
  <field id="endThread" type="Boolean" />
  <!-- responses -->
  <field id="errors" type="stringarray" />
  <field id="nonce" type="String" />
</interface>
<script type = "text/brightscript">
<![CDATA[
  Library "IMA3.brs"
]]>
</script>
</component>

ทริกเกอร์คอมโพเนนต์อินเทอร์เฟซจากโหมด

ต่อไป เพิ่มโค้ด BrightScript ที่จะคอยตรวจจับการโต้ตอบและทริกเกอร์ การเปลี่ยนแปลงในคอมโพเนนต์อินเทอร์เฟซ ดังนี้

  • หากต้องการรับเอาต์พุตจากคอมโพเนนต์อินเทอร์เฟซ ให้ใช้เครื่องมือสังเกตการณ์ฟิลด์ในช่องอินเทอร์เฟซที่เชื่อมโยงกับเอาต์พุตเหล่านั้น และแนบกับฟังก์ชันเรียกกลับในคอมโพเนนต์หลัก ทำเช่นนี้เมื่อลงทะเบียนคอมโพเนนต์เป็นครั้งแรก

  • หากต้องการส่งการโต้ตอบของผู้ใช้ไปยังคอมโพเนนต์อินเทอร์เฟซ ให้ใช้ตัวสังเกตช่องบนปุ่มที่คุณสร้างขึ้นก่อนหน้านี้เพื่อทริกเกอร์การเปลี่ยนแปลงในช่องอินเทอร์เฟซที่เชื่อมโยงกับคำสั่งเหล่านั้น

components/MainScene.xml

<?xml version="1.0" encoding="utf-8" ?>
<component name="MainScene" extends="Scene" initialFocus="requestNonceButton">
<children>
  <ButtonGroup>
    <button text="Request Nonce" id="requestNonceButton" />
    <button text="Send Ad Click" id="sendAdClickButton" />
    <button text="Send Ad Touch" id="sendAdTouchButton" />
    <button text="Send Playback Start" id="sendPlaybackStartButton" />
    <button text="Send Playback End" id="sendPlaybackEndButton" />
  </ButtonGroup>
  <Video id="YourVideoPlayer" width="720" height="480" focusable="true" />
</children>
<script type="text/brightscript">
<![CDATA[
  Function init()
    requestNonceButton = m.top.findNode("requestNonceButton")
    requestNonceButton.observeField("buttonSelected", "requestNonce")

    sendAdClickButton = m.top.findNode("sendAdClickButton")
    sendAdClickButton.observeField("buttonSelected", "sendAdClick")
    sendPlaybackStart = m.top.findNode("sendPlaybackStartButton")
    sendPlaybackStart.observeField("buttonSelected", "sendPlaybackStart")
    sendPlaybackEnd = m.top.findNode("sendPlaybackEndButton")
    sendPlaybackEnd.observeField("buttonSelected", "sendPlaybackEnd")

    loadImaSdk()
  End Function

  ' Initialize SDK Interface component and attach callbacks to field observers.
  Function loadImaSdk() as Void
    m.sdkTask = createObject("roSGNode", "PALInterface")
    m.sdkTask.observeField("errors", "onSdkLoadedError")
    m.sdkTask.observeField("nonce", "onNonceLoaded")
    print "Running load IMA task."
    m.sdkTask.control = "RUN"
  End Function

  Sub onSdkLoadedError(message as Object)
    print "----- errors in the sdk loading process --- ";message.getData()
  End Sub

  ' Callback triggered when Nonce is loaded.
  Sub onNonceLoaded(message as Object)
    nonce = m.sdkTask.nonce
    print "onNonceLoaded ";nonce
  End Sub

  Function requestNonceButtonPressed() As Void
    print "Request Nonce"
    ' Inform the SDK interface component to request a nonce.
    m.sdkTask.requestNonce = True
  End Function

  ' Action triggered on player start, either from user action or autoplay.
  Function sendPlaybackStart() As Void
    m.sdkTask.sendPlaybackStart = True
  End Function

  ' Action triggered on player end, either when content ends or the user exits
  ' playback of this content.
  Function sendPlaybackEnd() As Void
    m.sdkTask.sendPlaybackEnd = True
  End Function
]]>
</script>
</component>

เพิ่มวิธีโอนโฟกัส

จากนั้น ให้จับภาพการกดแป้นของผู้ใช้เพื่อโอนโฟกัสไปยังและออกจากองค์ประกอบวิดีโอ

components/MainScene.xml

...

<script type="text/brightscript">
<![CDATA[
  Function init()

    ...

    m.sendPlaybackStart = m.top.findNode("sendPlaybackStartButton")
    m.sendPlaybackStart.observeField("buttonSelected", "sendPlaybackStart")
    m.sendPlaybackEnd = m.top.findNode("sendPlaybackEndButton")
    m.sendPlaybackEnd.observeField("buttonSelected", "sendPlaybackEnd")

    m.transferFocusToVideoButton = m.top.findNode("transferFocusToVideoButton")
    m.transferFocusToVideoButton.observeField("buttonSelected", "transferFocusToVideo")

    ' Your video player set up to handle key press events.
    m.video = m.top.findNode("YourVideoPlayer")
    m.video.observeField("pressedKey", "onVideoKeyPress")

    loadImaSdk()
  End Function

  ...

  ' Action triggered on player end, either when content ends or the user exits
  ' playback of this content.
  Function sendPlaybackEnd() As Void
    m.sdkTask.sendPlaybackEnd = True
  End Function

  Function transferFocusToVideo() As Void
    m.video.setFocus(true)
  End Function

  Function onVideoKeyPress() As Void
    key = m.video.pressedKey
    If key = ""
      Return
    End If
    m.sdkTask.sendAdTouchKey = key

    ' If back or up is pressed, transfer focus back up to the buttons.
    If key = "back" or key = "up"
      m.transferFocusToVideoButton.setFocus(true)
    End If

    ' Reset so that we get the next key press, even if it's a repeat of the last
    ' key.
    m.video.pressedKey = ""
  End Function
]]>
</script>
</component>

เริ่มต้น PAL SDK และสร้าง nonceLoader

ตอนนี้คุณก็เริ่มสร้างตรรกะหลักของการติดตั้งใช้งาน PAL SDK ได้แล้ว ก่อนอื่นให้เริ่มต้น SDK จากเทรดแยกต่างหาก

components/PALInterface.xml

...
<script type = "text/brightscript">
<![CDATA[
  Library "IMA3.brs"

  Sub init()
    ' It is not possible to access roUrlTransfer on the main thread. Setting
    ' functionName to a function and then setting control to "RUN" causes that
    'function to run on a separate thread.
    m.top.functionName = "runPalThread"

    ' Loads the SDK on the current thread if it is not yet loaded.
    ' This blocks execution of other functions on this thread until the SDK is loaded.
    If m.sdk = Invalid
      m.sdk = new_imaSdk()
    End If

    m.nonceLoader = m.sdk.CreateNonceLoader()
  End Sub

  ' Starts the player event loop. This loop only terminates when "endThread" is sent.
  Function runPalThread() as Void
    ' Used for the player life cycle loop.
    m.top.endThread = False
    port = CreateObject("roMessagePort")

  End Function
]]>
</script>
</component>

ประมวลผลคำขอ Nonce

เมื่อสร้าง nonceLoader แล้ว คุณต้องจัดการคำขอ Nonce โดยแนบผู้สังเกตการณ์ลงในช่อง requestNonce การแนบผู้สังเกตการณ์นี้หลังจากที่เริ่มต้น nonceLoader แล้วเท่านั้น จะช่วยให้มั่นใจได้ว่ามีการจัดการคำขอ Nonce ในเทรด SDK และจะส่งคำขอ Nonce ได้ต่อเมื่อมี nonceLoader ที่ถูกต้องเท่านั้น

components/PALInterface.xml

...

  ' Starts the player event loop. This loop only terminates when "endThread" is sent.
  Function runPalThread() as Void
    ' Used for the player life cycle loop.
    m.top.endThread = False
    port = CreateObject("roMessagePort")

    ' Now that the nonceLoader exists, begin listening for nonce requests.
    m.top.observeField("requestNonce", m.port)

  End Function

  ' Requests a nonce from the PAL SDK.
  Function requestNonce() as Void
    nonceRequest = m.sdk.CreateNonceRequest()
    m.nonceManager = m.nonceLoader.loadNonceManager(nonceRequest)
    m.top.nonce = nonceManager.getNonce()
  End Function
]]>
</script>
</component>

ค่าเริ่มต้นของ NonceRequest.storageAllowed คือ true แต่คุณเปลี่ยนค่านี้ได้หลังจากรวบรวมความยินยอมที่เหมาะสมแล้ว เมธอด getConsentToStorage() เป็นตัวยึดตำแหน่งสำหรับวิธีการขอความยินยอมจากผู้ใช้ของคุณเอง ไม่ว่าจะด้วยการผสานรวมกับ CMP หรือตามวิธีการอื่นๆ ในการจัดการความยินยอมด้านพื้นที่เก็บข้อมูล

components/PALInterface.xml

...
<script type = "text/brightscript">
<![CDATA[
  Library "IMA3.brs"

  Sub init()
    ' It is not possible to access roUrlTransfer on the main thread. Setting
    ' functionName to a function and then setting control to "RUN" causes that
    'function to run on a separate thread.
    m.top.functionName = "runPalThread"

    ' Loads the SDK on the current thread if it is not yet loaded.
    ' This blocks execution of other functions on this thread until the SDK is loaded.
    If m.sdk = Invalid
      m.sdk = new_imaSdk()
    End If

    m.isConsentToStorage = getConsentToStorage()

    m.nonceLoader = m.sdk.CreateNonceLoader()
  End Sub

  ...

  ' Requests a nonce from the PAL SDK.
  Function requestNonce() as Void
    nonceRequest = m.sdk.CreateNonceRequest()

    ' Include changes to storage consent here.
    nonceRequest.storageAllowed = m.isConsentToStorage

    m.nonceManager = m.nonceLoader.loadNonceManager(nonceRequest)
    m.top.nonce = nonceManager.getNonce()
  End Function

ฟังสัญญาณวงจรการเล่นของผู้เล่น

เพื่อให้การผสานรวม PAL ส่งสัญญาณได้อย่างถูกต้อง คุณต้องตั้งค่าลูปเพื่อฟังสัญญาณตามวงจรของโปรแกรมเล่น

components/PALInterface.xml

...

    ' Now that the nonceLoader exists, begin listening for nonce requests.
    m.top.observeField("requestNonce", m.port)

    m.top.observeField("sendAdClick", m.port)
    m.top.observeField("sendAdTouchKey", m.port)
    m.top.observeField("sendPlaybackStart", m.port)
    m.top.observeField("sendPlaybackEnd", m.port)

    ' Setting endThread to true causes the while loop to exit.
    m.top.observeField("endThread", m.port)

    While Not m.top.endThread
      message = m.port.waitMessage(1000)
      If message = Invalid
        pollManager()
      Else If message.getField() = "requestNonce" And m.top.requestNonce = True
        requestNonce()
        m.top.requestNonce = False
      Else If message.getField() = "sendAdClick" And m.top.sendAdClick = True
        sendAdClick()
        m.top.sendAdClick = False
      Else If message.getField() = "sendAdTouchKey" And m.top.sendAdTouchKey <> ""
        sendAdTouch(m.top.sendAdTouchKey)
        m.top.sendAdTouchKey = ""
      Else If message.getField() = "sendPlaybackStart" And m.top.sendPlaybackStart = True
        sendPlaybackStart()
        m.top.sendPlaybackStart = False
      Else If message.getField() = "sendPlaybackEnd" And m.top.sendPlaybackEnd = True
        sendPlaybackEnd()
        m.top.sendPlaybackEnd = False
      End If
    End While
  End Function

  Function pollManager() as Void
    If m.nonceManager <> Invalid
      m.nonceManager.poll()
    End If
  End Function

  ' Requests a nonce from the PAL SDK.
  Function requestNonce() as Void
    nonceRequest = m.sdk.CreateNonceRequest()
    m.nonceManager = m.nonceLoader.loadNonceManager(nonceRequest)
    m.top.nonce = nonceManager.getNonce()
  End Function
]]>
</script>
</component>

ลงทะเบียน Listener สำหรับ sendPlaybackStart, sendPlaybackEnd, sendAdClick และ sendAdTouch

จากนั้น เรียก sendPlaybackStart ว่า "เริ่มโปรแกรมเล่นวิดีโอ" วิธีนี้จะเริ่มการเรียกไปยังเซิร์ฟเวอร์ของ Google แบบไม่พร้อมกันเพื่อรวบรวมสัญญาณที่จำเป็นสำหรับการตรวจสอบและตรวจหา IVT โทรหา sendPlaybackEnd เมื่อเล่นจบ โทร sendAdClick เพื่อตอบกลับการคลิกผ่านโฆษณา จากนั้น เรียกใช้ sendAdTouch สำหรับเหตุการณ์การแตะหรือการคลิก ของผู้ใช้ที่ไม่ผ่านการคลิกผ่าน

components/PALInterface.xml

...

  ' Requests a nonce from the IMA SDK.
  Function requestNonce() as Void
    nonceRequest = m.sdk.CreateNonceRequest()
    m.nonceManager = m.nonceLoader.loadNonceManager(nonceRequest)
    m.top.nonce = nonceManager.getNonce()
  End Function

  ' Registers an ad click using the IMA SDK.
  Function sendAdClick() as Void
    If m.nonceManager <> Invalid
      m.nonceManager.sendAdClick()
    End If
  End Function

  ' Registers an ad touch event using the IMA SDK.
  Function sendAdTouch(touch as String) as Void
    If m.nonceManager <> Invalid
      m.nonceManager.sendAdTouch(touch)
    End If
  End Function

  ' Registers the start of playback using the IMA SDK.
  Function sendPlaybackStart() as Void
    If m.nonceManager <> Invalid
      m.nonceManager.sendPlaybackStart()
    End If
  End Function

  ' Registers the end of playback using the IMA SDK.
  Function sendPlaybackEnd() as Void
    If m.nonceManager <> Invalid
      m.nonceManager.sendPlaybackEnd()
    End If
  End Function
]]>
</script>
</component>

แนบ Nonce กับคำขอโฆษณา

หากต้องการใช้ Nonce ที่คุณได้รับจากไลบรารี PAL ในแอปพลิเคชันเวอร์ชันที่ใช้งานจริง ให้เริ่มคำขอโฆษณาหลังจากสร้าง Nonce แล้วเท่านั้น จากนั้นเพิ่ม Nonce ต่อท้ายแท็กโฆษณาโดยใช้พารามิเตอร์ u_paln

components/MainScene.xml

...

  ' Callback triggered when Nonce is loaded.
  Sub onNonceLoaded(message as Object)
    nonce = m.sdkTask.nonce
    print "onNonceLoaded ";nonce
    makeAdRequest(nonce)
  End Sub

  Sub makeAdRequest(nonce)
    ' Sample ad tag URL used in this sample. Your apps method of getting this
    ' URL will likely be different.
    adTag = "https://pubads.g.doubleclick.net/gampad/ads?iu=/124319096/external/single_ad_samples"

    preparedTag = adTag + "&u_paln=" + nonce

    ' Implement custom ad request logic here.
    Print "ad tag with nonce ";preparedTag
  End Sub
...

เท่านี้ก็เรียบร้อย ตอนนี้คุณมีแอป Roku ที่สามารถขอ Nonce ของ PAL และลงทะเบียนเหตุการณ์เซสชันการเล่นกับ PAL SDK แล้ว

(ไม่บังคับ) ส่งสัญญาณ Google Ad Manager ผ่านเซิร์ฟเวอร์โฆษณาของบุคคลที่สาม

กำหนดค่าคำขอของเซิร์ฟเวอร์โฆษณาบุคคลที่สามสำหรับ Ad Manager

กำหนดค่าเซิร์ฟเวอร์โฆษณาบุคคลที่สามให้รวม Nonce ในคำขอของเซิร์ฟเวอร์ที่ส่งไปยัง Ad Manager ต่อไปนี้เป็นตัวอย่างของแท็กโฆษณาที่กำหนดค่าภายในเซิร์ฟเวอร์โฆษณาของบุคคลที่สาม

'https://pubads.serverside.net/gampad/ads?givn=%%custom_key_for_google_nonce%%&...'

ดูรายละเอียดเพิ่มเติมได้ในคู่มือการติดตั้งใช้งานฝั่งเซิร์ฟเวอร์ของ Google Ad Manager

Ad Manager จะมองหา givn= เพื่อระบุค่า Nonce เซิร์ฟเวอร์โฆษณาบุคคลที่สามต้องรองรับมาโครบางอย่างของตนเอง เช่น %%custom_key_for_google_nonce%% และแทนที่ด้วยพารามิเตอร์ Nonce ที่คุณระบุไว้ในขั้นตอนก่อนหน้า ข้อมูลเพิ่มเติมเกี่ยวกับวิธีดำเนินการนี้ควรอยู่ในเอกสารของเซิร์ฟเวอร์โฆษณาบุคคลที่สาม

เท่านี้ก็เรียบร้อย ตอนนี้คุณควรมีพารามิเตอร์ nonce ที่ส่งจาก PAL SDK ผ่านเซิร์ฟเวอร์ตัวกลาง จากนั้นจึงไปที่ Google Ad Manager วิธีนี้ช่วยให้คุณสร้างรายได้ ผ่าน Google Ad Manager ได้ดีขึ้น