Official Document Address

If you can read the document, there is no need to read further. That concludes this article.

The following is a record of the holes I locked in because I found that the contact customer service on my Mac was not what I wanted.

Enter the formal access process!!

Step 1: Register as a developer

Step 2: Create an application

These two steps suggest looking directly at the document. But there are a couple of things to keep in mind when creating an app

  • Application introduction: This is not only a brief introduction to the application, but also needs to indicate which interfaces will be used after the application is created, and for what purpose

    Introduction is given to illustrate the application of (your introduction), mainly used to interface are: 1, https://developers.e.qq.com/oauth/authorize through the interface to get the authorization_code; 2, call interface to https://api.e.qq.com/oauth/token for access_token and refresh_token; 3, the last in the call sends the request at https://api.e.qq.com/v1.0/campaigns/add

  • Callback address: This can be your company’s domain name or something, it can be incorrect and it doesn’t matter much.
  • After the application is created, some data will be sent to you in the mailbox. I did not use this data in the whole process, but directly used the data in the formal process. Basically still see oneself!

Once the application has been created and approved, you can proceed to the next steps.

Step 3: Authentication

Authentication is using a browser to invoke an interface

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

Among them:

  • Client_id: specifies the unique ID of the application created by the developer. This parameter is mandatory and can be viewed on the Application Management page.
  • Redirect_uri: callback address, which is provided and defined by the developer (the address primary field must be the same as the callback primary field registered when the developer creates the application). It is used to jump to and receive callback information. This parameter is mandatory.
  • State: a developer’s custom parameter that can be used to verify the validity of the request or pass other custom information.
  • Scope: Authorized capability range. This parameter is optional. If this parameter is not transmitted, the authorized scope is all the capabilities possessed by the current application.

The OAuth 2.0 authorization page is shown below:

https://www.test.com/?authorization_code=1066a051ea8fbaf011b892c202ae1c93&state=

https://www.test.com represents the callback address you filled in.

Authorization_code = 1066 a051ea8fbaf011b892c202ae1c93 this parameter will be in the next steps in the big role.

Click on your Tencent social platform -> Application Management -> Application Test and enter the application Test details page. The newly authorized account will be found in the binding account management bar at the bottom of the page.

I have two on my side because I authorized two accounts. Notice that there is a wide access account above the picture, this will be useful to you.

Step 4: Use authorization_code to obtain access_token and refresh_token

Tencent document about obtaining this or said very clear, specific can view the document. Just post a picture of my request:

The request for this interface is GET

Step 5: Report user behavior data

Before reporting user behavior data, it is necessary for wide communication personnel to create App user behavior data source in DMP background. This step will obtain user attribute data source ID, which will be used by the interface.

Requested address:

https://api.e.qq.com/v1.0/user_properties/add?access_token= < ACCESS_TOKEN > & timestamp = < timestamp > & nonce = < nonce > '

Let me explain the parameters

  • Access_token: data obtained by the previous interface
  • Timestamp: timestamp (second level)
  • Nonce: 32-bit random code

Request mode: POST

Request parameters:

The name of the type describe
Account_id (required) integer Promotion account ID, account ID with operation authority, including agent and advertiser account ID
User_action_set_id (required) integer User behavior source ID, unique ID assigned when the user behavior source is created using [user_action_sets interface]
The actions (required) struct[] Return an arraylist that cannot be larger than 50KB. The minimum length of the array is 1 and the maximum length is 50
Action_time (required) integer The point in time on the client when the behavior occurs. The unit is second. If this parameter is not specified, the minimum value is 0 and the maximum value is 2147483647
User_id (required) struct The user id
Hash_imei (required) string The IMEI device ID is lowercase and md5 encoding is performed. The length of the field ranges from 32 bytes to 32 bytes
Action_type (required) enum Standard behavior type, ‘CUSTOM’ indicates CUSTOM behavior type, enumeration list: { CUSTOM, REGISTER, VIEW_CONTENT, CONSULT, ADD_TO_CART, PURCHASE, ACTIVATE_APP, SEARCH, ADD_TO_WISHLIST, INITIATE_CHECKOUT, COMPLETE_ORDER, START_APP, RATE, PAGE_VIEW, RESERVATION, SHARE, APPLY, CLAIM_OFFER, NAVIGATE, PRODUCT_RECOMMEND, VISIT_STORE, TRY_OUT, DELIVER, SIGN_IN }

The above is just an explanation of the parameters

Let me give you a JSON example

    {
	"account_id": "<ACCOUNT_ID>"."user_action_set_id": "<USER_ACTION_SET_ID>"."actions": [{"action_time": 1535097324."user_id": {
                "hash_imei": "f9efca36a3c30e1cf28170d86ecbf5e9"
            },
            "action_type": "REGISTER",}}]Copy the code

So how do I get account_id? Two ways:

One is that when we get the access_token, we return an account_id field in the result data. Yes, that’s it.

The second one is the broadpoint account ID in the bound account management under the application details

User_action_set_id is the id of the user attribute data source, which is provided by the peer.

Action_time: timestamp (second level)

Hash_imei: MD5 encrypted string of the IMEI

Action_type: type to be reported this time

If the interface returns

{
    "code": 0."message": ""
}
Copy the code

Well, congratulations. It’s all worked out.

Now is the time to start coding, don’t worry, there are a few things to note.

For example, the access_token obtained above is expired, and refresh_token needs to be used to refresh the expiration date. How to refresh? See Tencent documentation. What’s the problem now? For example, if the docking test is passed today, and the package is online and approved, the access_token will expire two days later. How to deal with it? Some of you might say, well, let’s refresh the data interface when the user calls it. It’s possible, but I think it’s too frequent. Therefore, I asked the technical support of Tencent, and they suggested that the mechanism of refreshing access_token should be moved to the server side, that is, the server side should help us refresh the token once a day. Refreshing the access_token will not change the value of access_token. Only the validity period of the access_token changes. Then all we need to do on the client side is simply report the data.

The code should still be up (note: the following code was written by Kotlin) :

Entity class

class OCPABean(
        /** * Wide contact account ID, (can be found by yourself, or provided by the other party) */
        varaccountID: String? ./** * User behavior source ID, this is provided by the other party (wide access) */
        varuserActionSetId: String? ./** * Token obtained by code */
        vartoken: String? ./ * * * * /
        var channelIdList: List<String>?)
Copy the code

The following class is mainly used to initialize some of our necessary data

object OCPAStorage {
    /** * Stores the channel number corresponding to the app package name */
    var ocpaConfig = mutableMapOf<String, OCPABean>()

    init {
        initTest()
    }

    private fun initTest(a) {
        var list = listOf("getui1")
        var ocpaBean = OCPABean("83005"."177844"."5a42ed16464f8cda38c7e4ea000", list)
        ocpaConfig["com.baidu.test"] = ocpaBean
    }

}
Copy the code

Tools, providing some methods, note: currentRegisterNeedReport this method can be implemented according to their demand

object OCPAUtil {

    var ocpaBean: OCPABean? = null

    /** * Whether the current package name and channel need to be reported */
    fun currentRegisterNeedReport(context: Context): Boolean {
        var packageName = context.packageName
        var ocpaConfigMap = OCPAStorage.ocpaConfig
        var keys = ocpaConfigMap.keys
        if (packageName in keys) {
            var ocpaBean = ocpaConfigMap[packageName]
            varchannelIdList = ocpaBean? .channelIdListvar channelName = ChannelUtil.getChannelName(context)
            if(channelIdList? .contains(channelName) ==true) {
                this@OCPAUtil.ocpaBean = ocpaBean
                return true}}return false
    }

    /** * gets the current second-level timestamp */
    fun getTimeStamp(a): String {
        var timeStamp = System.currentTimeMillis() / 1000
        return timeStamp.toString()
    }

    /** * generates 32-bit encoding *@return string
     */
    fun getUUID(a): String {
        return UUID.randomUUID().toString().trim().replace("-"."")}}Copy the code

The following class is mainly to do network requests, reporting interface, using the Okhttp3 network framework

object OCPAHttpClient {

    const val SP_CONSTANT = "OCPA_SP_CONSTANT"

    fun register(context: Context) {
        var spConstant = FRSharedPreferences.getStringNotClear(SP_CONSTANT, "")
// var spConstant = ""
        if (TextUtils.isEmpty(spConstant) && OCPAUtil.currentRegisterNeedReport(context)) {
            httpRegister()
        }
    }

    private fun httpRegister(a) {
        varocpaBean = OCPAUtil.ocpaBean ocpaBean? .let {val okHttpClient = OkHttpClient.Builder()
                    .connectTimeout(30, TimeUnit.SECONDS)
                    .writeTimeout(30, TimeUnit.SECONDS)
                    .readTimeout(30, TimeUnit.SECONDS)
                    .build()
            // Get the current second-level timestamp
            var timeStamp = OCPAUtil.getTimeStamp()
            // Construct a URL
            var baseUrl = "https://api.e.qq.com/v1.0/user_actions/add?access_token=${ocpaBean.token}&timestamp=$timeStamp&nonce=${OCPAUtil.getUUID()}"
            var dataJsonObject = JSONObject()
            // Construct parameters
            try {
                dataJsonObject.put("account_id", ocpaBean.accountID)
                dataJsonObject.put("user_action_set_id", ocpaBean.userActionSetId)
                var actionJA = JSONArray()

                var actionJO = JSONObject()
                actionJO.put("action_time", timeStamp)
                actionJO.put("action_type"."REGISTER")

                var userIdJO = JSONObject()
                varimei = FRDeviceUtil.getPhoneInfo() imei? .let { userIdJO.put("hash_imei", FREncryptUtil.string2MD5(imei))
                }

                actionJO.put("user_id", userIdJO)
                actionJA.put(actionJO)
                dataJsonObject.put("actions", actionJA)
            } catch (e: JSONException) {
                e.printStackTrace()
            }

            var requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), dataJsonObject.toString())
            var request = Request.Builder()
                    .url(baseUrl)
                    .post(requestBody)
                    .build()
            okHttpClient.newCall(request).enqueue(object : Callback {
                override fun onFailure(call: Call? , e:IOException?). {
                    FRLog.e("log"."OCPA registration failed")}override fun onResponse(call: Call? , response:Response?). {
                    FRSharedPreferences.setStringNotClear(SP_CONSTANT, SP_CONSTANT)
                    FRLog.e("log", response? .body()? .string()) } }) } } }Copy the code

MD5 encryption class:

public class FREncryptUtil {
    /** * use MD5 encryption **@paramInStr Character string * to be encrypted@returnEncrypted string *@throwsNoSuchAlgorithmException has no such algorithm for generating message digests *@throws UnsupportedEncodingException
     */
    public static String string2MD5(String inStr) {
        if (TextUtils.isEmpty(inStr))
            return "";
        MessageDigest md5 = null;
        try {
            md5 = MessageDigest.getInstance("MD5");
        } catch (Exception e) {
            System.out.println(e.toString());
            e.printStackTrace();
            return "";
        }
        char[] charArray = inStr.toCharArray();
        byte[] byteArray = new byte[charArray.length];

        for (int i = 0; i < charArray.length; i++)
            byteArray[i] = (byte) charArray[i];
        byte[] md5Bytes = md5.digest(byteArray);
        StringBuffer hexValue = new StringBuffer();
        for (int i = 0; i < md5Bytes.length; i++) {
            int val = ((int) md5Bytes[i]) & 0xff;
            if (val < 16)
                hexValue.append("0");
            hexValue.append(Integer.toHexString(val));
        }
        returnhexValue.toString(); }}Copy the code

The method of obtaining Imei: This method may be a little different from the time when it is used.

/** * Obtain the IMEI of the mobile phone **@return* /
public static String getPhoneInfo(Context context) {
    TelephonyManager tm = null;
    try {
        tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
        return tm.getDeviceId();
    } catch (Exception e) {
        return ""; }}Copy the code

All right, that’s it. Don’t forget to ask the backstage to refresh the token!!