当前位置: 首页 > article >正文

华为HarmonyOS打造开放、合规的广告生态 - 激励广告

场景介绍

激励广告是一种全屏幕的视频广告,用户可以选择点击观看,以换取相应奖励。

接口说明

接口名

描述

loadAd(adParam: AdRequestParams, adOptions: AdOptions, listener: AdLoadListener): void

请求单广告位广告,通过AdRequestParams、AdOptions进行广告请求参数设置,通过AdLoadListener监听广告请求回调。

showAd(ad: Advertisement, options: AdDisplayOptions, context?: common.UIAbilityContext): void

展示广告,通过AdDisplayOptions进行广告展示参数设置。

开发步骤

  1. 获取OAID。

    如果想要为用户更精准的推送广告,可以在请求参数AdRequestParams中添加oaid属性。

    如何获取OAID参见获取OAID信息。

    说明

    使用以下示例中提供的测试广告位必须先获取OAID信息。

  2. 请求单广告位广告。

    需要先创建一个AdLoader对象,通过AdLoader的loadAd方法请求广告,最后通过AdLoadListener,来监听广告的加载状态。

    请求广告关键参数如下所示:

    请求广告参数名

    类型

    必填

    说明

    adType

    number

    请求广告类型,激励广告类型为7。

    adId

    string

    广告位ID。

    • 如果仅调测广告,可使用测试广告位ID:testx9dtjwj8hp。
    • 如果要接入正式广告,则需要申请正式的广告位ID。可在应用发布前进入流量变现官网,点击“开始变现”,登录鲸鸿动能媒体服务平台进行申请,具体操作详情请参见展示位创建。

    oaid

    string

    开放匿名设备标识符,用于精准推送广告。不填无法获取到个性化广告。

    示例代码如下所示:
     
      
    1. import { advertising, identifier } from '@kit.AdsKit';
    2. import { common } from '@kit.AbilityKit';
    3. import { hilog } from '@kit.PerformanceAnalysisKit';
    4. import { BusinessError } from '@kit.BasicServicesKit';
    5. @Entry
    6. @Component
    7. struct Index {
    8. private ads: Array<advertising.Advertisement> = [];
    9. private context = getContext(this) as common.UIAbilityContext;
    10. private oaid: string = '';
    11. aboutToAppear() {
    12. try {
    13. // 使用Promise回调方式获取OAID
    14. identifier.getOAID().then((data: string) => {
    15. this.oaid = data;
    16. hilog.info(0x0000, 'testTag', '%{public}s', 'Succeeded in getting adsIdentifierInfo by promise');
    17. }).catch((error: BusinessError) => {
    18. hilog.error(0x0000, 'testTag', '%{public}s', `Failed to get adsIdentifierInfo, message: ${error.message}`);
    19. })
    20. } catch (error) {
    21. hilog.error(0x0000, 'testTag', '%{public}s', `Catch err, code: ${error.code}, message: ${error.message}`);
    22. }
    23. }
    24. build() {
    25. Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
    26. Row() {
    27. Button('requestAd').onClick(() => {
    28. let load: advertising.AdLoader = new advertising.AdLoader(this.context);
    29. this.requestAd(load);
    30. }).width('45%')
    31. }
    32. }
    33. }
    34. private requestAd(adLoader: advertising.AdLoader): void {
    35. const adRequestParam: advertising.AdRequestParams = {
    36. // 广告类型:激励广告
    37. adType: 7,
    38. // 'testx9dtjwj8hp'为测试专用的广告位ID,App正式发布时需要改为正式的广告位ID
    39. adId: 'testx9dtjwj8hp',
    40. // 开放匿名设备标识符
    41. oaid: this.oaid
    42. };
    43. const adOption: advertising.AdOptions = {
    44. // 设置是否请求非个性化广告
    45. nonPersonalizedAd: 0,
    46. // 是否允许流量下载0:不允许,1:允许,不设置以广告主设置为准
    47. allowMobileTraffic: 0,
    48. // 是否希望根据 COPPA 的规定将您的内容视为面向儿童的内容: -1默认值,不确定 0不希望 1希望
    49. tagForChildProtection: -1,
    50. // 是否希望按适合未达到法定承诺年龄的欧洲经济区 (EEA) 用户的方式处理该广告请求: -1默认值,不确定 0不希望 1希望
    51. tagForUnderAgeOfPromise: -1,
    52. // 设置广告内容分级上限: W: 3+,所有受众 PI: 7+,家长指导 J:12+,青少年 A: 16+/18+,成人受众
    53. adContentClassification: 'A'
    54. };
    55. const adLoaderListener: advertising.AdLoadListener = {
    56. onAdLoadFailure: (errorCode: number, errorMsg: string) => {
    57. hilog.error(0x0000, 'testTag', '%{public}s',
    58. `Failed to request ad, message: ${errorMsg}, error code: ${errorCode}`);
    59. },
    60. onAdLoadSuccess: (ads: Array<advertising.Advertisement>) => {
    61. hilog.info(0x0000, 'testTag', '%{public}s', `Succeeded in requesting ad`);
    62. this.ads.push(...ads);
    63. },
    64. };
    65. adLoader.loadAd(adRequestParam, adOption, adLoaderListener);
    66. }
    67. }

  3. 事件订阅。

    开发者需要在App中订阅com.huawei.hms.pps.action.PPS_REWARD_STATUS_CHANGED事件来监听激励广告页面变化并接收奖励信息。示例代码中的订阅方法registerPPSReceiver()需要在每次展示广告前调用 。

    在订阅到公共事件后,可以从CommonEventData的parameters参数中使用"reward_ad_status"作为key值获取激励广告页面变化状态,使用"reward_ad_data"作为key值获取奖励信息,属性rewardType用来获取奖励物品的名称,rewardAmount用来获取奖励物品的数量。

    示例代码如下所示:

     
      
    1. import { commonEventManager, BusinessError } from '@kit.BasicServicesKit';
    2. import { hilog } from '@kit.PerformanceAnalysisKit';
    3. const KEY_REWARD_DATA = "reward_ad_data";
    4. const KEY_REWARD_STATUS = "reward_ad_status";
    5. export class RewardAdStatusHandler {
    6. // 用于保存创建成功的订阅者对象,后续使用其完成订阅及退订的动作
    7. private subscriber: commonEventManager.CommonEventSubscriber | null = null;
    8. // 订阅方法,需要在每次展示广告前调用
    9. public registerPPSReceiver(): void {
    10. if (this.subscriber) {
    11. this.unRegisterPPSReceiver();
    12. }
    13. // 订阅者信息
    14. const subscribeInfo: commonEventManager.CommonEventSubscribeInfo = {
    15. events: ['com.huawei.hms.pps.action.PPS_REWARD_STATUS_CHANGED'],
    16. // publisherBundleName被设置为"com.huawei.hms.adsservice",这意味着只有来自该包名的事件才会被订阅者接受和处理。
    17. // 如果没有明确声明publisherBundleName,那么订阅者可能会收到来自其它包名的伪造事件,从而导致安全性问题或误导。
    18. publisherBundleName: 'com.huawei.hms.adsservice'
    19. };
    20. // 创建订阅者回调
    21. commonEventManager.createSubscriber(subscribeInfo, (err: BusinessError, commonEventSubscriber:
    22. commonEventManager.CommonEventSubscriber) => {
    23. if (err) {
    24. hilog.error(0x0000, 'testTag', '%{public}s',
    25. `createSubscriber error, code: ${err.code}, message: ${err.message}`);
    26. return;
    27. }
    28. hilog.info(0x0000, 'testTag', '%{public}s', 'Succeeded in creating subscriber');
    29. this.subscriber = commonEventSubscriber;
    30. // 订阅公共事件回调
    31. if (!this.subscriber) {
    32. hilog.warn(0x0000, 'testTag', '%{public}s', 'Need create subscriber');
    33. return;
    34. }
    35. commonEventManager.subscribe(this.subscriber, (err: BusinessError, commonEventSubscriber:
    36. commonEventManager.CommonEventData) => {
    37. if (err) {
    38. hilog.error(0x0000, 'testTag', '%{public}s', `Subscribe error, code: ${err.code}, message: ${err.message}`);
    39. } else {
    40. hilog.info(0x0000, 'testTag', '%{public}s', 'Subscribe data');
    41. const status: string = commonEventSubscriber?.parameters?.[KEY_REWARD_STATUS];
    42. switch (status) {
    43. case AdStatus.AD_OPEN:
    44. hilog.info(0x0000, 'testTag', '%{public}s', 'onAdOpen');
    45. break;
    46. case AdStatus.AD_CLICKED:
    47. hilog.info(0x0000, 'testTag', '%{public}s', 'onAdClick');
    48. break;
    49. case AdStatus.AD_CLOSED:
    50. hilog.info(0x0000, 'testTag', '%{public}s', 'onAdClose');
    51. this.unRegisterPPSReceiver();
    52. break;
    53. case AdStatus.AD_REWARDED:
    54. const rewardData: Record<string, string | number> = commonEventSubscriber?.parameters?.[KEY_REWARD_DATA];
    55. const rewardType: string = rewardData?.rewardType as string;
    56. const rewardAmount: number = rewardData?.rewardAmount as number;
    57. hilog.info(0x0000, 'testTag', '%{public}s',
    58. `onAdReward, rewardType: ${rewardType}, rewardAmount: ${rewardAmount}`);
    59. break;
    60. case AdStatus.AD_VIDEO_START:
    61. hilog.info(0x0000, 'testTag', '%{public}s', 'onAdVideoStart');
    62. break;
    63. case AdStatus.AD_COMPLETED:
    64. hilog.info(0x0000, 'testTag', '%{public}s', 'onAdCompleted');
    65. break;
    66. default:
    67. break;
    68. }
    69. }
    70. });
    71. });
    72. }
    73. // 取消订阅
    74. public unRegisterPPSReceiver(): void {
    75. commonEventManager.unsubscribe(this.subscriber, (err: BusinessError) => {
    76. if (err) {
    77. hilog.error(0x0000, 'testTag', '%{public}s', `Unsubscribe error, code: ${err.code}, message: ${err.message}`);
    78. } else {
    79. hilog.info(0x0000, 'testTag', '%{public}s', 'Succeeded in unsubscribing');
    80. this.subscriber = null;
    81. }
    82. });
    83. }
    84. }
    85. enum AdStatus {
    86. AD_OPEN = 'onAdOpen',
    87. AD_CLICKED = 'onAdClick',
    88. AD_CLOSED = 'onAdClose',
    89. AD_REWARDED = 'onAdReward',
    90. AD_VIDEO_START = 'onVideoPlayBegin',
    91. AD_COMPLETED = 'onVideoPlayEnd'
    92. }

  4. 展示广告。

    ads为步骤2请求到的广告信息,调用showAd方法来展示广告。示例代码如下所示:
     
      
    1. import { advertising } from '@kit.AdsKit';
    2. import { common } from '@kit.AbilityKit';
    3. @Entry
    4. @Component
    5. struct Index {
    6. private context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
    7. // 步骤2中请求到的广告内容
    8. private ads: Array<advertising.Advertisement> = [];
    9. private displayOptions: advertising.AdDisplayOptions = {
    10. // 激励广告视频播放是否静音
    11. mute: true
    12. };
    13. build() {
    14. Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
    15. Row() {
    16. Button('showAd').onClick(() => {
    17. this.showAd();
    18. }).width('45%')
    19. }
    20. }
    21. }
    22. private showAd() {
    23. let load: advertising.AdLoader = new advertising.AdLoader(this.context);
    24. // 此处ads[0]表示请求到的第一个广告,用户根据实际情况选择
    25. advertising.showAd(this.ads[0], this.displayOptions, this.context);
    26. }
    27. }

校验激励广告服务端验证回调

服务端验证回调是指鲸鸿动能平台发送给媒体服务器的网址请求,其中带有特定的查询参数,用来通知媒体服务器某位用户因为与激励视频广告互动而应予以奖励,从而规避欺骗的行为。

奖励用户

  • 在给用户发奖励时,要把握好用户体验和奖励验证之间的平衡。由于服务器端回调会存在延迟的情况,因此我们建议客户端立即奖励用户,同时在收到服务器端回调时对所有奖励进行验证。这种做法可确保奖励符合发放条件,同时提供良好的用户体验。
  • 对于某些应用而言,奖励是否达到发放条件非常重要,用户可适当接受延迟。这时,推荐做法是等待服务器端回调完成验证,再向用户发放奖励。

校验服务端验证回调

说明

App上架至华为应用市场(AppGallery)时间超过12小时才可以收到回调。

  1. 设置激励广告的奖励配置。

    您在鲸鸿动能媒体服务平台上申请激励视频广告位时选择“媒体管理(点击媒体名)> 新增展示位 > 选择激励视频(点击下一步,进入编辑页面)”,设置奖励类型和奖励数量,并点击“高级设置”,设置服务器端验证的URL。如下图:

  2. (可选)设置自定义数据customData和userId。

    ads为步骤2请求到的广告信息,调用showAd方法来展示广告。

    您在App中展示激励广告之前设置自定义数据customData和userId。示例代码如下所示:

     
      
    1. import { advertising } from '@kit.AdsKit';
    2. import { common } from '@kit.AbilityKit';
    3. @Entry
    4. @Component
    5. struct Index {
    6. private context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
    7. // 步骤2中请求到的广告内容
    8. private ads: Array<advertising.Advertisement> = [];
    9. private displayOptions: advertising.AdDisplayOptions = {
    10. // 激励广告视频播放是否静音
    11. mute: true,
    12. // 设置自定义数据
    13. customData: 'CUSTOM_DATA',
    14. // 设置自定义数据
    15. userId: '1234567'
    16. };
    17. build() {
    18. Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
    19. Row() {
    20. Button('showAd').onClick(() => {
    21. this.showAd();
    22. }).width('45%')
    23. }
    24. }
    25. }
    26. private showAd() {
    27. let load: advertising.AdLoader = new advertising.AdLoader(this.context);
    28. // 此处ads[0]表示请求到的第一个广告,用户根据实际情况选择
    29. advertising.showAd(this.ads[0], this.displayOptions, this.context);
    30. }
    31. }

    说明

    如果没有设置customData和userId,不影响发放奖励事件上报但是服务端验证的参数中没有这两个字段。如果设置customData和userId,必须在展示广告之前设置并且URLEncode之后,长度不超过1024个字符,否则影响服务端验证。

  3. 获取要验证的内容。

    用户观看完激励广告时,鲸鸿动能平台服务端会把需要验证的参数以及keyId和sign传给媒体提供的URL:https://www.example.com/feedback(即步骤1中配置的验证URL)。请求体样例:

     
      
    1. {
    2. "adId" : "testx9dtjwj8hp",
    3. "data" : "CUSTOM_DATA",
    4. "keyId" : "12345678",
    5. "rewardAmount" : "10",
    6. "rewardName" : "金币",
    7. "sign" : "OA33u6mypnhE4hbmF32N/ibYi1uXt72nDDyYMwjDI6JXVVFKePZYo4F7Fuk2MaG......",
    8. "uniqueId" : "3361626337333932313435313430373438383561376265636130393939313166",
    9. "userId" : "1234567"
    10. }

    服务器端验证回调查询参数说明:

    参数名称

    类型

    是否必选

    描述

    adId

    String

    激励视频广告位ID

    data

    String

    自定义数据字符串

    keyId

    String

    验证回调的密钥

    rewardAmount

    String

    奖励数量

    rewardName

    String

    奖励奖品

    sign

    String

    回调的签名

    uniqueId

    String

    获奖事件生成的十六进制的标识符

    userId

    String

    用户ID

  4. 组装验证参数

    验证内容(除sign、keyId)格式顺序如下:

    adId={adId}&data={data}&rewardAmount={rewardAmount}&rewardName={rewardName}&uniqueId={uniqueId}&userId={userId}

    其中‘{}’里面表示参数的值,且参数顺序不能变。如果参数为null或者空字符串,则URL中不拼接该参数。然后用SHA256计算散列值,得到paramContentData。示例代码如下所示:

     
      
    1. String adId = request.getParameter("adId");
    2. String data = request.getParameter("data");
    3. ...
    4. String userId = request.getParameter("userId");
    5. String param = "adId=" + adId + "&data=" + data + "&rewardAmount=" + rewardAmount + "&rewardName=" + rewardName + "&uniqueId=" + uniqueId + "&userId=" + userId;
    6. String sha256Value = Sha256Util.digest(param);
    7. byte[] paramContentData = sha256Value.getBytes(Charset.forName("UTF-8"));
  5. 获取公钥列表。

    a. 在鲸鸿动能媒体服务平台上查看对应的帐户信息时选择“账户”。

    通过点击上图所示的“获取密钥”按钮弹出如下所示的弹框,获取“开发者ID”和“密钥”。

    b. 您根据应用分发区域不同,需要使用对应站点的接口URL去获取公钥列表,不同站点对应的接口URL如下所示:

    • 中国:https://ppscrowd-drcn.op.hicloud.com/action-lib-track/publickeys

    将body通过密钥进行HMAC-SHA256加密得到签名,替换到Authorization中,并设置“开发者ID”和Authorization到Header中。示例代码如下所示:

     
      
    1. String data = "";
    2. String url = "https://ppscrowd-dre.op.dbankcloud.com/action-lib-track/publickeys";
    3. String authorization = "Digest validTime=\"{0}\", response=\"{1}\"";
    4. // 开发者ID
    5. String userId = "YOUR_PUBLISHER_ID";
    6. // 密钥
    7. String key = "YOUR_KEY";
    8. HttpClient httpclient = HttpClients.createDefault();
    9. HttpGet request = new HttpGet();
    10. try {
    11. // 将body通过密钥进行HMAC-SHA256加密得到签名,替换到Authorization中
    12. String validTime = String.valueOf(System.currentTimeMillis());
    13. String body = validTime + ":/publickeys";
    14. byte[] keyBytes = Base64.decodeBase64(key);
    15. byte[] bodyBytes = body.getBytes(Charsets.UTF_8);
    16. Mac mac = Mac.getInstance("HmacSHA256");
    17. SecretKey secretKey = new SecretKeySpec(keyBytes, "HmacSHA256");
    18. mac.init(secretKey);
    19. byte[] signatureBytes = mac.doFinal(bodyBytes);
    20. String signature = (signatureBytes == null) ? null : Hex.encodeHexString(signatureBytes);
    21. authorization = MessageFormat.format(authorization, validTime, signature);
    22. // 设置开发者ID和Authorization到Header中
    23. request.setURI(new URI(url));
    24. request.setHeader("userId", userId);
    25. request.setHeader("Authorization", authorization);
    26. HttpResponse response = httpclient.execute(request);
    27. data = EntityUtils.toString(response.getEntity());
    28. } catch (Exception e) {
    29. System.out.println(e.getMessage());
    30. }

    返回data消息体(publicKey已匿名化):

     
      
    1. {
    2. "keys": [
    3. {
    4. "keyId":"12345678",
    5. "publicKey":"LS0tLS1*******************************************************"
    6. },
    7. {
    8. "keyId": "22345678",
    9. "publicKey":"LS0tLS1*******************************************************"
    10. }
    11. ]
    12. }

    返回消息结构体:

    参数名称

    类型

    是否必选

    描述

    keys

    List<key>

    返回公钥列表

    • key结构体:

    参数名称

    类型

    是否必选

    描述

    keyId

    String

    密钥ID

    publicKey

    String

    公钥

  6. 执行验证。

    a. 根据keyId从公钥列表中找到对应的base64编码后的publicKey。

    b. 将paramContentData、publicKey、sign和SHA256withRSA数字签名算法的入参,执行验证。

    示例代码如下所示:

     
      
    1. public static boolean verify(byte[] data, String publicKey, String sign, String signatureAlgorithm) {
    2. try {
    3. byte[] keyBytes = base64Decode(publicKey);
    4. X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
    5. KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    6. PublicKey publicK = keyFactory.generatePublic(keySpec);
    7. Signature signature = Signature.getInstance(signatureAlgorithm);
    8. signature.initVerify(publicK);
    9. signature.update(data);
    10. return signature.verify(base64Decode(sign));
    11. } catch (InvalidKeyException | SignatureException | UnsupportedEncodingException | InvalidKeySpecException | NoSuchAlgorithmException e) {
    12. return false;
    13. }
    14. }
    15. private static byte[] base64Decode(String encoded) throws UnsupportedEncodingException {
    16. return Base64.decodeBase64(encoded.getBytes("UTF-8"));
    17. }

http://www.kler.cn/a/379803.html

相关文章:

  • AI - 使用LangChain构建简单LLM应用程序
  • 一二三应用开发平台自定义查询设计与实现系列3——通用化重构
  • RK3568开发板Openwrt文件系统构建
  • Hive自定义函数—剔除周日周六(小时级别)
  • SSH详解
  • 【CSS】——基础入门常见操作
  • SpringSession源码分析
  • 水仙花求和
  • 使用 pytorch 运行预训练模型的框架
  • D58【python 接口自动化学习】- python基础之异常
  • 不需要复制粘贴,重复内容如何使用Mac快速完成输入
  • 初始JavaEE篇——多线程(5):生产者-消费者模型、阻塞队列
  • 【Mac】Screen Recorder by Omi Mac:Omi录屏专家
  • 从最小作用量原理推导牛顿三大定律
  • 相机硬触发
  • 小红书笔记详情API接口系列(概述到示例案例)
  • Mac上的免费压缩软件-FastZip使用体验实测
  • Vue3的router和Vuex的学习笔记整理
  • WPF使用Prism框架首页界面
  • 语言模型的采样方法
  • 企业培训考试系统源码
  • 深入理解Spring、SpringMVC、SpringBoot和Spring Cloud的区别与用法
  • 服务器数据恢复—RAID5阵列硬盘坏道掉线导致存储不可用的数据恢复案例
  • 【前端开发入门】JavaScript快速入门--数据操作
  • [vulnhub] DC:9
  • antdesignvue + AWS-S3实现Minio大文件分片上传