苹果内购-WWDC
App Store Server API
苹果提供了以下这些 Server API
API简介
查询用户订单的收据
GET https://api.storekit.itunes.apple.com/inApps/v1/lookup/{orderId}
苹果提供了以下这些 Server API
查询用户订单的收据
GET https://api.storekit.itunes.apple.com/inApps/v1/lookup/{orderId}
还是先找官方文档...
google支付需要在服务端记录并验证订单,防止伪造订单,这里记录一下服务端校验订单
Google Pay主要支付流程:
相关信息
项目使用的工具:
项目使用语言极其版本:
特别说明
uni-app x,是下一代 uni-app,是一个跨平台应用开发引擎。
uni-app x 是一个庞大的工程,它包括uts语言、uvue渲染引擎、uni的组件和API、以及扩展机制。
uts是一门类ts的、跨平台的、新语言。uts在iOS端编译为swift、在Android端编译为kotlin、在Web端编译为js。
在Android平台,uni-app x 的工程被编译为kotlin代码,本质上是换了vue写法的原生kotlin应用,在性能上与原生kotlin一致。
引入自定义持久层框架jar包
本质是对JDBC进行封装
加载配置文件
创建两个JavaBean(容器对象)
解析配置文件,填充容器对象
创建SqlSessionFactory接口及DefaultSqlSessionFactory
创建SqlSession接口和DefaultSqlSession实现类
创建Executor接口和实现类SimpleExecutor
ConcurrentHashMap的构造器和HashMap的构造器基本相同
ConcurrentHashMap键或者值为空会抛出空指针异常,put方法开始先求取hash
final V putVal(K key, V value, boolean onlyIfAbsent) {
if (key == null || value == null) throw new NullPointerException();
int hash = spread(key.hashCode());
...
}
static final int spread(int h) {
return (h ^ (h >>> 16)) & HASH_BITS;
}
HashMap在创建对象时,可的调用构造器有三种,无参构造器(常用),初始化容量大小,初始化容量和加载因子
初始化的时候,
当谈论游戏数据时,以下是一些常见的术语和概念:
留存率(Retention Rate):
留存率是指在特定时间段内,玩家继续留在游戏中的百分比。通常以日留存率(玩家在游戏中连续两天或更多天的百分比)和周留存率(玩家在游戏中连续一周或更多周的百分比)来衡量。
生命价值(Lifetime Value,LTV):
LTV是指玩家在其整个游戏生命周期内为游戏公司带来的预期收入。它通常考虑了玩家的购买习惯、留存率和付费金额等因素。
在完成日常任务时,有时候需要根据不同的目的,自己定制化完成log日志的输出,通过配置文件实现日志输出存在一定的局限性,可以通过代码来,定制化实现下面的目标
package com.game.server.config;
import java.text.SimpleDateFormat;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.FileAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.varia.LevelRangeFilter;
public class LoggerFactory {
// 防止频繁创建Logger对象
private static volatile Map<String, List<Object>> logMap = new ConcurrentHashMap<String, List<Object>>();
public static Logger getLogger(String baseDir, String appId, String type) {
SimpleDateFormat formatter = new SimpleDateFormat("'_'yyyyMMdd");
String date = formatter.format(new Date());
if (StringUtils.isEmpty(baseDir) || StringUtils.isEmpty(appId)) {
throw new IllegalArgumentException("cannot be empty");
}
// 防止map过大不按时间存储{ key: [date_A, logger_A] }
String key = baseDir + "/" + appId + "/gameserver_" + appId + "_" + type;
List<Object> list = logMap.get(key);
if (list == null) {
// 存储新数据
try {
Logger logger = createLogger(key, type, date);
return logger;
} catch (Exception e) {
e.printStackTrace();
}
}
// 比较时间
String saveDate = String.valueOf(list.get(0));
if (saveDate.equals(date)) {
Logger logger = (Logger) list.get(1);
return logger;
} else {
try {
// 更新数据
Logger logger = createLogger(key, type, date);
return logger;
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
/**
* 添加同步锁,并进行双端校验,防止重复创建
*
* @param key
* @param type
* @param date
* @return
*/
private synchronized static Logger createLogger(String key, String type, String date) {
// 双端检索,防止重复创建
List<Object> list = logMap.get(key);
if (list != null) {
String saveDate = String.valueOf(list.get(0));
if (saveDate.equals(date)) {
Logger logger = (Logger) list.get(1);
return logger;
}
}
String logFilePath = key + date;
// 添加Appender到日志记录器
Logger logger = Logger.getLogger("com.game.server.config.LoggerFactory." + type + date);
FileAppender appender = new FileAppender();
// 设置输出文件名
appender.setFile(logFilePath);
// 设置输出格式
appender.setLayout(new PatternLayout("%m%n"));
// 设置Appender的阈值级别
appender.setThreshold(Level.INFO);
appender.activateOptions();
LevelRangeFilter filterInfo = new LevelRangeFilter();
filterInfo.setLevelMin(Level.INFO);
filterInfo.setLevelMax(Level.ERROR);
appender.addFilter(filterInfo);
logger.addAppender(appender);
list = new ArrayList<>();
list.add(date);
list.add(logger);
logMap.put(key, list);
return logger;
}
}