IOS内购数据拉取

目标:拉取app store connect 内购数据拉取,自己做数据报表。

1:api秘钥

  接口需要token,token生成需要秘钥。参考官方文档:https://developer.apple.com/documentation/appstoreconnectapi/creating_api_keys_for_app_store_connect_api 。

 

 vendorNumber在这个界面查找:

 

2:生成token

根据上面保存的东西生成token。官方文档: https://developer.apple.com/documentation/appstoreconnectapi/generating_tokens_for_api_requests

这里用 java+vertx框架实现的:

  private JWTAuth iniJwt() {
        JsonObject header = new JsonObject()
                .put("alg", "ES256")
                .put("kid", keyID) //key_id
                .put("typ", "JWT");
        JWTOptions options = new JWTOptions()
                .setAlgorithm("ES256")
                .setHeader(header);
        Vertx vertx = ConfigHelp.vertx;
        //加载下载的 p8文件
        Buffer iosPayKeyBuffer = vertx.fileSystem().readFileBlocking("resource/AuthKey_A2BRZ78255.p8");
        Buffer keyBuffer = iosPayKeyBuffer;
        JWTAuthOptions config = new JWTAuthOptions()
                .addPubSecKey(new PubSecKeyOptions()
                        .setAlgorithm("ES256")
                        .setBuffer(keyBuffer))
                .setJWTOptions(options);
        return JWTAuth.create(vertx, config);
    }
private String getToken(String parameter) {
long sT = (int) (System.currentTimeMillis() / 1000);
long eT = sT + 60 * 15; //过期时间

String params = "GET " + parameter;
List<String> scope = new ArrayList<>(1);
scope.add(params);

JsonObject body = new JsonObject()
.put("iss", issuer) //issuer
.put("iat", sT)
.put("exp", eT)
.put("aud", "appstoreconnect-v1")
.put("scope", scope);

JWTAuth provider = iniJwt();
return provider.generateToken(body);
}

 

3:下载销售报告

财务报告是每月生成,销售报告是每天生成。参考文档:https://developer.apple.com/documentation/appstoreconnectapi/download_sales_and_trends_reports

 这里请求数据post使用的是 okhttp3,所以需要添加 pom.xml:

        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>4.10.0</version>
        </dependency>

 请求代码

//参数
String curDate = "2023-04-20"; //需要下载报表的日期
String vendorNumber = "21432532456";//通过上面消息得到的 vendor_number
String params = "/v1/salesReports?filter[frequency]=DAILY&filter[reportSubType]=SUMMARY" +
"&filter[reportType]=SALES" +
"&filter[vendorNumber]=" + vendorNumber + "&filter[reportDate]=" + curDate;
//请求数据
String token = getToken(params);
String url = "https://api.appstoreconnect.apple.com" + params;
Request request = new Request.Builder()
                .url(url)
                .method("GET", null)
                .addHeader("Authorization", "Bearer " + token)
                .addHeader("Accept", "application/a-gzip")
                .build();

 try {
      OkHttpClient client = new OkHttpClient().newBuilder()
                    .addInterceptor(new UnzippingInterceptor())
                    .build();
      Response response = client.newCall(request).execute();

      String body = Objects.requireNonNull(response.body()).string();
      System.out.println(body);
 } catch (IOException e) {
            System.out.println(e);
 }

 

 注意:请求数据返回的是zip文件,所以这里需要解压。addInterceptor(new UnzippingInterceptor())就是用来解压zip的。

package com.gcms.postdata.pay;

import okhttp3.Headers;
import okhttp3.Interceptor;
import okhttp3.Response;
import okhttp3.internal.http.RealResponseBody;
import okio.GzipSource;
import okio.Okio;
import org.jetbrains.annotations.NotNull;

import java.io.IOException;

public class UnzippingInterceptor implements Interceptor {
    @NotNull
    @Override
    public Response intercept(@NotNull Chain chain) throws IOException {
        Response response = chain.proceed(chain.request());
        return unzip(response);
    }

    // copied from okhttp3.internal.http.HttpEngine (because is private)
    private Response unzip(final Response response) throws IOException
    {
        if (response.body() == null)
        {
            return response;
        }

        //check if we have gzip response
        String contentEncoding = response.headers().get("Content-Encoding");

        if(contentEncoding == null)
            return response;

        //this is used to decompress gzipped responses
        if (contentEncoding.equals("gzip") || contentEncoding.equals("agzip"))
        {
            Long contentLength = response.body().contentLength();
            GzipSource responseBody = new GzipSource(response.body().source());
            Headers strippedHeaders = response.headers().newBuilder().build();
            return response.newBuilder().headers(strippedHeaders)
                    .body(new RealResponseBody(response.body().contentType().toString(), contentLength, Okio.buffer(responseBody)))
                    .build();
        }
        else
        {
            return response;
        }
    }
}
View Code

 最后得到的body就是解析后的数据。

 

 4:解析数据

 得到的数据是按照excel形式在txt文件中展示的,需要解析出自己需要的数据

       String[] separated = body.split("\n"); //使用 \n 拆分行,
        int len = separated.length;
        for (int i = 1; i < len; i++) { //遍历每一行数据
            String[] row = separated[i].split("\t"); //使用 \t 拆分列,得到每一列数据

            String packId = row[17];//包名
            float proceeds = Float.parseFloat(row[8]); //收益
            if (proceeds == 0)
                continue;

            float price = Float.parseFloat(row[15]);  //定价
            //其他字段
        }

 

 具体的报告字段参考:https://developer.apple.com/help/app-store-connect/reference/summary-sales-report

 

5:完成。具体的数据自己储存到数据库,自己使用。

 

注意:得到的数据各种货币都有,需要自己转为一种类型的货币。自己查找汇率。

举个例子:在计算或者展示时,统一转为美金计算。units * proceeds * 汇率 = 内购

        //转为美金的汇率
        switch (city) {
            case "GBP":
                return 1.20469f;
            case "AUD":
                return 0.66903f;
            case "USD":
                return 1;
            case "CHF":
                return 1.07279f;
            case "CAD":
                return 0.73338f;
        }

 

posted @ 2023-04-27 10:58  Joy_CShow  阅读(262)  评论(0编辑  收藏  举报