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;
}
}
}
最后得到的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内购数据拉取的更多相关文章
- iOS - 内购总结
如果有人以后要在做内购这一块.希望可以好好的阅读这篇文章,虽然不是字字珠玑.但是也是本人亲人趟过了无数的坑,希望可以对大家有所帮助! 下面是在研究工程中遇到的问题(iOS 内购的流程如下 1 ...
- IOS内购支付server验证模式
IOS 内购支付两种模式: 内置模式 server模式 内置模式的流程: app从app store 获取产品信息 用户选择须要购买的产品 app发送支付请求到app store app store ...
- IOS内购支付服务器验证模式
IOS 内购支付两种模式: 内置模式 服务器模式 内置模式的流程: app从app store 获取产品信息 用户选择需要购买的产品 app发送支付请求到app store app store 处理支 ...
- Unity苹果(iOS)内购接入(Unity内置IAP)
https://www.jianshu.com/p/4045ebf81a1c Unity苹果(iOS)内购接入(Unity内置IAP) Kakarottog ...
- 苹果IOS内购二次验证返回state为21002的坑
项目是三四年前的老项目,之前有IOS内购二次验证的接口,貌似很久都没用了,然而最近IOS的妹子说接口用不了,让我看看啥问题.接口流程时很简单的,就是前端IOS在购买成功之后,接收到receipt后进行 ...
- iOS 内购遇到的坑
一.内购沙盒测试账号在支付成功后,再次购买相同 ID 的物品,会提示如下内容的弹窗.您以购买过此APP内购项目,此项目将免费恢复 原因: 当使用内购购买过商品后没有把这个交易事件关,所以当我们再次去购 ...
- iOS 内购相关
iOS 内购相关 下面总结一下过往订阅和内购的项目的代码方面的实现细节和注意事项,特别是掉单方面的处理. 后台的协议.商品ID.银行卡.内购类型.沙盒账号测试人员都由运营或者产品在苹果后台中申请处理. ...
- iOS 内购讲解
一.总说内购的内容 1.协议.税务和银行业务 信息填写 2.内购商品的添加 3.添加沙盒测试账号 4.内购代码的具体实现 5.内购的注意事项 二.协议.税务和银行业务 信息填写 2.1.协议.税务和银 ...
- iOS-IAP内购的那些事(iOS内购漏单的问题)
前言 说起内购,其实挺令开发者厌烦的,原因呢,先不说漏单的问题,首先苹果要扣除30%的销售额哦,可恨不?(我觉得可恨),有些想办法先隐藏掉第三方支付(支付宝.微信等),等项目上线了,再跳过内购使用第三 ...
- IOS内购--后台PHP认证
参考网址:https://blog.csdn.net/que_csdn/article/details/80861408 http://www.php.cn/php-weizijiaocheng-39 ...
随机推荐
- 深入掌握FastAPI与OpenAPI规范的高级适配技巧
title: 深入掌握FastAPI与OpenAPI规范的高级适配技巧 date: 2025/03/30 01:16:11 updated: 2025/03/30 01:16:11 author: c ...
- 【Ubuntu】ARM交叉编译开发环境解决“没有那个文件或目录”问题
[Ubuntu]ARM交叉编译开发环境解决"没有那个文件或目录"问题 零.起因 最近在使用Ubuntu虚拟机编译ARM程序,解压ARM的GCC后想要启动,报"没有那个文件 ...
- 【Win32】VC6 Visual C/C++ 6.0 修改程序图标
零.需求 就想给自己的C程序加个图标,好看些 一.解决 1.操作步骤 1.新建一个资源脚本 2.在新建的脚本上右键,选择插入 3.选择Icon,点新建或者引入,如果你没有准备图标点新建,有的话直接点引 ...
- 使用PyMuPDF对pdf文件插入文字时 遇到配置本地的字体文件缺仍然使用默认Helvetica字体问题
背景 昨天收到的新需求,一份文件从其他部门发起,进行一些文字填写后盖章,再到我们部门,我们接收到的是pdf文件,所以需要在pdf文件中进行修改,插入当日日期等文字.但有要求字体必须和原文档字体相同. ...
- 2025dsfz集训Day11:数位DP、状态压缩DP、单调队列优化DP
Day11:数位DP.状压DP.单调队列优化DP 经典题目:AccodersP2195 |[一本通提高数位动态规划]Amount of Degrees 题意: 求出区间 \([x,y]\) 中满足下面 ...
- 9.9K star!大模型原生即时通信机器人平台,这个开源项目让AI对话更智能!
嗨,大家好,我是小华同学,关注我们获得"最新.最全.最优质"开源项目和高效工作学习方法 "高稳定.支持插件.多模态 - 大模型原生即时通信机器人平台" 项目亮点 ...
- 关于网传微信聊天记录提取工具"留痕"盗取个人信息的分析
今天早上看到一篇文章,是关于一个微信聊天记录提取工具泄露个人信息的内容,于是我就好奇,看了一下作者的 github,然后也是自己小小的分析了一下 1.官方地址 Github: https://gith ...
- 代码随想录第二十七天 | Leecode 455. 分发饼干、 376. 摆动序列、 53. 最大子数组和
Leecode 455. 分发饼干 题目描述 假设你是一位很棒的家长,想要给你的孩子们一些小饼干.但是,每个孩子最多只能给一块饼干. 对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的 ...
- 【HUST】网安纳米|2023年研究生纳米技术考试参考
目录 1 纳米材料是什么 2 纳米材料的结构特性 3 纳米结构的其他特性 4 纳米结构的检测技术 5 纳米材料的应用 打印建议:PPT彩印(这样重点比较突出),每面12张PPT,简单做一下关键词目录, ...
- 信息资源管理综合题之“某国企投资IT应用人员减少但生成率没有实质性变化的IT黑洞问题”
一.某大型国企在IT应用上投资了2000万美元,虽然蓝领工人数量大幅减少,但实际生产率并未有实质性变化 1.企业在IT应用上的巨额投资并未达到预期目标的这种现象被称为什么? 2.产生这现象的原因有哪些 ...