接入文档地址:https://developers.e.qq.com/docs/start 

由于腾讯的广告业务做的太大,所以接起来还挺麻烦的,比起百度难接入。页面上的客服也是爱答不理的,反正等了一下午也没回我消息!就自己看,也不知道投放部门准备怎么接,就让开发自己看文档!看的头大!!!

 

android广点通接口文档 广点通接入_redis

这是腾讯给出的文档说明。

1、用 公用 qq (这个不用说了为啥了吧)注册为开发者后,创建应用程序等着审核,创建的几个应用程序不要相似度太高,要不通过不了,一般一个就够了。或者一个申请一种权限。

2、创建数据源,(可以通过可视化界面申请创建,请看官方文档)

上传行为数据。重点说下这个步骤:

这个步骤的目的是为了在管理后台知道我们有多少用户的转化率,大概是为了腾讯更好的推广产品吧。

要想上传行为数据,必须有access-token,这个token有两种获取方式:

1、直接获取

2、通过refresh_token获取

这两种方式有何区别:

第一种建议第一次的时候使用,因为获取的时候需要授权码,授权码是通过回调带回到自己填写的地址的,当带回到自己填写的地址后,我们就可以使用redis等工具把它暂存器来,然后调用获取access-token方法,这个时候我们就可以去获取access-token和 refresh_token,然后把这两者都存起来,每经过23小时左右(access-token 有效期24小时)使用refresh_token 刷新一次 access-token,这样就不会再出现access-token 过期的情况了。

另外:难免出现意外情况,再token过期的时候,可以使用 Springboot 的邮件系统给自己或者组员发送一个带有授权验证的超链接,任何一个组员或者全部成员看到后,都可以手动认证(因为腾讯做了图片的滑块校验),最后跳转到自定义的授权成功页面即可,就不再影响业务了!

我们会及时上报行为数据,

我们会再接到用户的点击行为的时候,把需要的上报链接封装好,预留一些字段,如时间戳和token等,等用户激活的时候再替换下链接做个上传。

在开发人员这里需要的几个接口或自动任务

1、授权成功的回调接口

https://developers.e.qq.com/oauth/authorize?client_id=123456&redirect_uri=https://www.example.com&state=&scope=ADS_MANAGEMENT

这是腾讯给出的引导客户进行授权认证时的链接,里面有一个redirect_url,这个算是需要开发的第一个接口,当认证成功后,腾讯还会回调的这个地址,示例如下

http://www.example.com/response?authorization_code=6a6b6c6d&state=112233 

我们需要把这个 authorization_code 字段保存下,用来请求 access_token。

2、广告点击行为接收接口 即feedback-url

这个用来获取用户的点击行为数据,在这里我们也可以定义自己的参数,如我们想知道这些数据是在哪投放的广告,可以加个 adcome=tencent 它会在调用的时候给我们带回来,这样我们就可以区分了。完整的url示例如下

http://www.example.com/feedback_url?adcome=tencent

3、自动任务使用refresh_token 刷新 access_token 

这个可以使用 Springboot 开发,我们可以使用定时的自动任务 多少毫秒刷新一次,如我的代码像这样:

android广点通接口文档 广点通接入_接入总结_02

 我们是把这些需要的token存入了redis里面,这样我们使用的时候直接去取就行了。

另外需要注意的是:我们开发的程序难免会有意外情况,这时候我们最好加个邮件提醒, 像我们发邮件,在邮件里面注明链接,这样相关人员或者开发者本人收到邮件后,可以直接点击去获取最新的 au_code。

目前还不是很完善,等待激活程序搞定后再来补充!目前项目还未上线! 

***********************************************************************************************

已经开始推广,有个小问题

数据对不上渠道

解决方法:数据上报的时候用了自定义的行为id字段,导致对不上,去掉即可。

页面上的客服依然那么的不懂业务!!!!

 

注意:在创建应用的时候,无需申请太多的权限,一般只需要数据上报权限即可,在说明里面最好别写测试等字眼,否则不容易被审核通过!

大部分关键源码如下:xxx 为公司名,YYY 为项目名 请酌情替换


AuthController.java


package com.XXX.YYY.controller;

import com.XXX.YYY.schedulertask.GetAndRefreshTokenTask;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;


@Controller
@RequestMapping("/")
@Slf4j
public class AuthController {

    @Autowired
    private GetAndRefreshTokenTask task;

    // redis 操作类
    @Autowired
    private RedisTemplate redisTemplate;

    // refresh_token access_token 的键
    private final String TOKEN_KEY ="tf_token_key";
    // 修改支持多账户 规则 authorization_code_+账户id  state_+账户id
    private final String AUTHORIZATION_CODE_PREFIX ="authorization_code_";
    private final String AUTHORIZATION_CODE ="authorization_code";
    private final String STATE_PREFIX ="state_";
    private final String STATE="state";


    /**
     * 授权成功跳转页面
     * 特殊说明:目前请求授权地址为:
     * 安卓 https://developers.e.qq.com/oauth/authorize?client_id=xxxxxxxx&redirect_uri=http://YYY.XXX.com/tf_auth&state=xxx_tf_auth_ida
     * IOS https://developers.e.qq.com/oauth/authorize?client_id=xxxxxxxx&redirect_uri=http://YYY.XXX.com/tf_auth&state=xxx_tf_auth_idb
     * @param mv 视图
     * @param request 请求
     * @return 成功页面
     */
    @GetMapping("/tf_auth")
    public ModelAndView test(ModelAndView mv,HttpServletRequest request) {
        String domain="http://YYY.XXX.com/tf_auth";
        String url = domain+"?"+request.getQueryString();
        log.info("获取 authorization_code 的url: "+url);
        // 分账户
        String state = request.getParameter(STATE).trim();
        // 操作类
        HashOperations<String, String, Object> hashOperations = redisTemplate.opsForHash();
        mv.setViewName("authSuccess");
        String authorization_code = request.getParameter(AUTHORIZATION_CODE);

        String accountId = state.substring(state.lastIndexOf("_")+1);
        hashOperations.put(TOKEN_KEY, AUTHORIZATION_CODE_PREFIX+accountId, authorization_code);
        hashOperations.put(TOKEN_KEY, STATE_PREFIX+accountId, state);

        // 授权成功更新下 token
        task.autoTaskJob();
        return mv;
    }
}

2、GetAndRefreshTokenTask.java

package com.XXX.YYY.schedulertask;

import com.alibaba.fastjson.JSONObject;
import com.XXX.YYY.utils.HttpUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;

@Component
@Slf4j
public class GetAndRefreshTokenTask {
    // refresh_token access_token 的键
    private final String TOKEN_KEY ="tf_token_key";
    // hash key
    private final String ACCESS_TOKEN ="access_token";
    private static final String REFRESH_TOKEN ="refresh_token";
    private final String AUTHORIZATION_CODE ="authorization_code";

    // 通过 Authorization Code 获取 Access Token 或刷新 Access Token
    private static final String GET_TOKEN_URL="https://api.e.qq.com/oauth/token?";

    private String ACCOUNT_PREFIX ="aso.tencent.account";

    // 请求需要的几个字段
    private static final String CLIENT_ID="client_id";
    private static final String CLIENT_SECRET ="client_secret";
    private static final String CLIENT_ID_VALUE="1108034188";
    private static final String CLIENT_SECRET_VALUE="xLfe83U0xzwHCXLl";
    // 请求的类型,可选值: authorization_code (授权码方式获取 token )、 refresh_token (刷新 token )
    private static final String GRANT_TYPE="grant_type";
    private static final String REDIRECT_URI="redirect_uri";
    private static final String REDIRECT_URI_VALUE="http://YYY.XXX.com/tf_auth";

    // 直接获取
    private static String getAccessTokenUrl = "";
    // 通过 refresh_token u获取
    private static String getAccessTokenFromRefreshTokenUrl = "";

    static {
        StringBuilder sb = new StringBuilder(GET_TOKEN_URL);
        sb.append(CLIENT_ID);
        sb.append("=");
        sb.append(CLIENT_ID_VALUE);
        sb.append("&");
        sb.append(CLIENT_SECRET);
        sb.append("=");
        sb.append(CLIENT_SECRET_VALUE);
        sb.append("&");
        sb.append(GRANT_TYPE);
        sb.append("=");
        sb.append("grant_type_value");
        getAccessTokenFromRefreshTokenUrl = sb.toString();
        sb.append("&");
        sb.append(REDIRECT_URI);
        sb.append("=");
        sb.append(REDIRECT_URI_VALUE);
        getAccessTokenUrl = sb.toString();
    }

    // 发件人地址
    @Value("${spring.mail.fromMail.addr}")
    private String from;

    // 收件人地址
    @Value("${spring.mail.toMail.addr}")
    private String toMail;

    @Autowired
    private TemplateEngine templateEngine;

    @Autowired
    private Environment env;

    @Autowired
    private JavaMailSender mailSender;

    @Autowired
    private RedisTemplate redisTemplate;
    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");

    // 23小时执行更新一次
    @Scheduled(fixedRate = 82800000)
    public void reportCurrentTime() {
        autoTaskJob();
    }
    // 封装成方法 便于认证成功也可以调用
    public void autoTaskJob(){
        System.out.println("现在时间:" + dateFormat.format(new Date()));
        HashOperations<String, String, Object> hashOperations = redisTemplate.opsForHash();

        String[] accountArr = env.getProperty(ACCOUNT_PREFIX).split(",");
        for(String account : accountArr){
            String  getTokenType = hashOperations.get(TOKEN_KEY, REFRESH_TOKEN+"_"+account) == null?"access":"refresh";
            String requestUrl = "";
            if("access".equals(getTokenType)){
                String authorization_code = hashOperations.get(TOKEN_KEY, AUTHORIZATION_CODE+"_"+account).toString();
                requestUrl = getAccessTokenUrl.replace("grant_type_value",AUTHORIZATION_CODE );
                requestUrl += "&"+AUTHORIZATION_CODE+"="+authorization_code;
            }else {
                String refresh_token = hashOperations.get(TOKEN_KEY, REFRESH_TOKEN+"_"+account).toString();
                requestUrl = getAccessTokenFromRefreshTokenUrl.replace("grant_type_value",REFRESH_TOKEN );
                requestUrl += "&"+REFRESH_TOKEN+"="+refresh_token;
            }

            String returnMsg = HttpUtil.httpClientGet(requestUrl);
            Map map = JSONObject.parseObject(returnMsg,Map.class);
            if("0".equals(map.get("code").toString())){
                Map data = (Map)map.get("data");
                String access_token = data.get(ACCESS_TOKEN).toString();
                String refresh_token = data.get(REFRESH_TOKEN).toString();

                hashOperations.put(TOKEN_KEY, ACCESS_TOKEN+"_"+account, access_token);
                hashOperations.put(TOKEN_KEY, REFRESH_TOKEN+"_"+account, refresh_token);

                log.info(account+":更新token成功!");
            }
            else {
                // 发送邮件提醒重新认证
                sendReAuthMail(account);
                log.error(account+":token更新失败!");
            }
        }

    }

    // 发送重新认证邮件
    private void sendReAuthMail(String content){
        //创建邮件正文
        Context context = new Context();
        context.setVariable("id", content);
        String emailContent = templateEngine.process("emailTemplate", context);
        sendHtmlMail( toMail,"广点通需要重新认证,请尽快处理!", emailContent);
    }

    /**
     * 发送html邮件 目前支持发送给单个接收者
     * @param to 收件人
     * @param subject 邮件主题
     * @param content 邮件内容
     */
    private void sendHtmlMail(String to, String subject, String content) {
        MimeMessage message = mailSender.createMimeMessage();
        try {
            //true表示需要创建一个multipart message
            MimeMessageHelper helper = new MimeMessageHelper(message, true);
            helper.setFrom(from);
            helper.setTo(to);
            helper.setSubject(subject);
            helper.setText(content, true);

            mailSender.send(message);
            log.info("html邮件发送成功");
        } catch (MessagingException e) {
            log.error("发送html邮件时发生异常!", e);
        }
    }
}

3、HttpUtil.java

package com.XXX.YYY.utils;

import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

public class HttpUtil {
    public static String  httpClientGet(String url) {
        String str = "";
        try{
            // 获取http客户端
            CloseableHttpClient client = HttpClients.createDefault();
            // 通过httpget方式来实现我们的get请求
            HttpGet httpGet = new HttpGet(url);
            // 通过client调用execute方法,得到我们的执行结果就是一个response,所有的数据都封装在response里面了
            CloseableHttpResponse Response = client.execute(httpGet);
            // HttpEntity
            // 是一个中间的桥梁,在httpClient里面,是连接我们的请求与响应的一个中间桥梁,所有的请求参数都是通过HttpEntity携带过去的
            // 所有的响应的数据,也全部都是封装在HttpEntity里面
            HttpEntity entity = Response.getEntity();
            // 通过EntityUtils 来将我们的数据转换成字符串
            str = EntityUtils.toString(entity, "UTF-8");
            // EntityUtils.toString(entity)
            System.out.println(str);
            // 关闭
            Response.close();
            return str;
        }
        catch (Exception ex){
            ex.printStackTrace();
            return str;
        }

    }
}

4、application.yml

server:
  port: 8081
    # 邮件发送设置
spring:
    mail:
      host: smtp.xxx.com
      username: name@staff.xxx.com
      password: passwd
      default-encoding: UTF-8
      fromMail:
        addr: name@staff.xxx.com
      toMail:
        addr: daguang0822@126.com

  # ida idb 分别为申请的应用的id 七位数字 storeid1 storeid2 分别为上报的数据库的id 十位数字
  tencent:
    # ida 安卓第一个账户 qq: qq1  idb IOS第一个账户 qq:qq2
    account: ida,idb
    user_action_set_id_ida: storeid1
    user_action_set_id_idb: storeid2

5、emailTemplate.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" >
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>广点通重新认证</title>
</head>
<body>
    <br/>
    <h3>账号异常,请及时处理</h3>
    <br/>
    如果您看到这封邮件!点击<a th:href="@{ https://developers.e.qq.com/oauth/authorize?client_id=xxxxxxxx&redirect_uri=http://XXX.YYY.com/tf_auth&state=xxx_tf_auth_{id}(id=${id})}">广点通链接</a>进行认证!
    提示:qq和账户的对应关系
    <br/>
    qq:qq1 账户:ida
    <br/>
    qq: qq2 账户:idb
 <br/>
<h3>谢谢!</h3>
</body>
</html>