目标:拉取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;
}
}
}

最后得到的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;
}

IOS内购数据拉取的更多相关文章

  1. iOS - 内购总结

        如果有人以后要在做内购这一块.希望可以好好的阅读这篇文章,虽然不是字字珠玑.但是也是本人亲人趟过了无数的坑,希望可以对大家有所帮助!  下面是在研究工程中遇到的问题(iOS 内购的流程如下 1 ...

  2. IOS内购支付server验证模式

    IOS 内购支付两种模式: 内置模式 server模式 内置模式的流程: app从app store 获取产品信息 用户选择须要购买的产品 app发送支付请求到app store app store ...

  3. IOS内购支付服务器验证模式

    IOS 内购支付两种模式: 内置模式 服务器模式 内置模式的流程: app从app store 获取产品信息 用户选择需要购买的产品 app发送支付请求到app store app store 处理支 ...

  4. Unity苹果(iOS)内购接入(Unity内置IAP)

    https://www.jianshu.com/p/4045ebf81a1c Unity苹果(iOS)内购接入(Unity内置IAP) Kakarottog                       ...

  5. 苹果IOS内购二次验证返回state为21002的坑

    项目是三四年前的老项目,之前有IOS内购二次验证的接口,貌似很久都没用了,然而最近IOS的妹子说接口用不了,让我看看啥问题.接口流程时很简单的,就是前端IOS在购买成功之后,接收到receipt后进行 ...

  6. iOS 内购遇到的坑

    一.内购沙盒测试账号在支付成功后,再次购买相同 ID 的物品,会提示如下内容的弹窗.您以购买过此APP内购项目,此项目将免费恢复 原因: 当使用内购购买过商品后没有把这个交易事件关,所以当我们再次去购 ...

  7. iOS 内购相关

    iOS 内购相关 下面总结一下过往订阅和内购的项目的代码方面的实现细节和注意事项,特别是掉单方面的处理. 后台的协议.商品ID.银行卡.内购类型.沙盒账号测试人员都由运营或者产品在苹果后台中申请处理. ...

  8. iOS 内购讲解

    一.总说内购的内容 1.协议.税务和银行业务 信息填写 2.内购商品的添加 3.添加沙盒测试账号 4.内购代码的具体实现 5.内购的注意事项 二.协议.税务和银行业务 信息填写 2.1.协议.税务和银 ...

  9. iOS-IAP内购的那些事(iOS内购漏单的问题)

    前言 说起内购,其实挺令开发者厌烦的,原因呢,先不说漏单的问题,首先苹果要扣除30%的销售额哦,可恨不?(我觉得可恨),有些想办法先隐藏掉第三方支付(支付宝.微信等),等项目上线了,再跳过内购使用第三 ...

  10. IOS内购--后台PHP认证

    参考网址:https://blog.csdn.net/que_csdn/article/details/80861408 http://www.php.cn/php-weizijiaocheng-39 ...

随机推荐

  1. CSAPP学习笔记——Chapter10,11 系统级I/O与网络编程

    CSAPP学习笔记--Chapter10,11 系统级I/O与网络编程 Chapter10 系统级I/O 系统级I/O这一章的内容,主要可以通过这张图概括: Unix I/O模型是在操作系统内核中实现 ...

  2. 一文速通Python并行计算:04 Python多线程编程-多线程同步(上)—基于条件变量、事件和屏障

    一文速通 Python 并行计算:04 Python 多线程编程-多线程同步(下)-基于条件变量.事件和屏障 摘要: 本文介绍了 Python 多线程同步的三种机制:条件变量(Condition).事 ...

  3. 【Python】介绍以及环境搭建

    Python简介 Python介绍 Python是时下最流流.最火爆的编程语言之一,具体原因如下: 简单.易学,适应人群广泛 免费.开源 应用领域广泛 备注:以下知名框架均是Python语言开发. G ...

  4. 正反代理-nginx安装

    参考文章:https://www.cnblogs.com/ysocean/p/9384877.html 先预祝一下成功 废话不多说,开始吧,步骤不多 下载地址 https://nginx.org/en ...

  5. 🎀在线设计平台-mastergo

    简介 MasterGo是一款面向现代团队的专业在线UI/UX设计平台,它支持界面设计.交互原型制作.设计系统管理和团队协作等功能.这款工具旨在提升设计师的工作效率,并促进团队成员之间的高效沟通与合作. ...

  6. [笔记]这些超级好用的html标签和css属性

    1.sup.sub 上标.下标,直接看下面的例子吧 A<sub>2</sub> 4<sup>2</sup> 42 A2 2.伪类属性的love hate ...

  7. ElasticSearch学习文档

    中文文档:https://doc.codingdict.com/elasticsearch/ Elastic 官方网站:Free and Open Search: The Creators of El ...

  8. 抽象方法(abstract)、虚方法(virtual)及接口(interface)

    抽象方法(abstract).虚方法(virtual)及接口(interface) 抽象方法(abstract) 定义:abstract关键词标记的方法--抽象方法 特征: 抽象方法只能定义在抽象类里 ...

  9. Manus爆火,我发现平替开源项目OpenManus带你玩转AI智能体开发,无需邀请码!

    嗨,大家好,我是小华同学,关注我们获得"最新.最全.最优质"开源项目和高效工作学习方法 "在AI技术日新月异的今天,OpenManus像一把打开智能体开发大门的万能钥匙, ...

  10. 代码随想录第九天 | Leecode 151.翻转字符串里的单词、Leecode 28. 找出字符串中第一个匹配项的下标、Leecode 459.重复的子字符串

    Leecode 151.翻转字符串里的单词 题目链接:https://leetcode.cn/problems/reverse-words-in-a-string/description/ 题目描述 ...