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 ...
随机推荐
- HarmonyOS NEXT 基于原生能力获取视频缩略图
大家好,我是 V 哥. 不得不佩服 HarmonyOS NEXT 原生能力的强大,如果你想在 鸿蒙 APP 开发中获取视频缩略图,不用依赖第三方库,就可以高效和稳定的实现,AVMetadataHelp ...
- BUUCTF---childRSA(费马引理)
题目 点击查看代码 from random import choice from Crypto.Util.number import isPrime, sieve_base as primes fro ...
- 事件监听、焦点--java进阶day03
1.事件 按钮是组件,点击后就会重新游戏 对于这种点击了组件之后,有逻辑触发的操作,就是事件 2.事件中的专有名词 绑定监听也就是绑定监视,是真正组织代码逻辑的地方 要有绑定监听就需要监听器,今天学习 ...
- 强化学习(on-policy)同步并行采样(on-line)的并行化效率分析
在强化学习中(on-line)的算法如果是on-policy的算法都是需要较大的采样样本的,因此采样的效率往往对整个算法运行效率有着自关重要的影响,在deepmind(Google)公司的强化学习的并 ...
- IDEA target中没有class文件/target中有class没有yml文件/yml文件不显示叶子
target中没有class文件.表现为文件显示红波浪线,但是点进去自己又好了,但是编译会说找不到.点进入target文件夹发现没有class文件,只有yml文件或者什么都没有 解决方法:rebuil ...
- 查看MySQL是否安装成功
1)安装了Windows Service:MySQL80,并且已经启动. 2)安装了MySQL软件.安装位置为:C:\Program Files\MySQL (默认路径) (MySQL文件下放的是软 ...
- tp5 分页权限权限设置显示
$adminid = cookie("adminid"); $shop_id=$this->get_shop_id(); if($adminid==1){ $uid = in ...
- Cline技术分析:prompt如何驱动大模型对本地文件实现自主变更
prompt如何驱动大模型对本地文件实现自主变更 在AI技术快速发展的今天,编程方式正在经历一场革命性的变革.从传统的"人写代码"到"AI辅助编程",再到&qu ...
- Delegate的Target,Method
在 C# 中,Delegate 是一种引用方法的类型,可以将方法视为对象进行传递和操作.Delegate 类型的实例可以用来引用一个或多个方法,然后可以将这些引用作为参数传递给其他方法,或者用来调用这 ...
- save could notbe completed!!!
错误描述:ctrl+s报错 eg: 原因及解决:eclipse正使用的字符集GBK,不能支持所有代码的映射,需要删掉不符合和字符或者换个字符集--所以还是比较推荐后者:Projext->Prop ...