ios内购开发的坑

  |   0 评论   |   0 浏览

如果你的ios需要有虚拟付费功能,那么你必须需要使用ios的内购,才能审核通过。

在对接ios内购的时候,还是遇到了很多的坑,经过一番努力,最终解决上线了。

需要在苹果上面申请产品包,这里产品包申请有个坑(还有可能就是对苹果的产品不了解):
1、消耗型产品
ios返回的in_app数据只有一个;
2、订阅型产品
ios返回的in_app数据是一个购买历史列表;

坑1:刚开始的时候,我们申请了消耗型产品,这个类型的产品在in_app里只返回当前购买的数据。后来申请被拒了,说充值会员不能用这个类型的产品,就换成订阅型产品,结果验证收据时in_app返回购买历史列表。因为之前看网上的文档,没有提到in_app里面是列表,然后代码是获取in_app列表里面的第一个数据,在做订单价格比较的时候,一直匹配不上。为什么要做价格比较?因为苹果的支付成功结果是返回给客户端的,为了安全起见,需要验证苹果的收据信息里面的产品id跟我们的订单表的产品id是否一样。本来想比较收据里的流水号跟我们订单的流水号是否相同,但是发现苹果压根没返回我们提交的流水号。

坑2:in_app数据变成列表之后,后台要验证订单信息,发现不太好验证,后来发现客户端可以获取到苹果收据返回的transactionId,in_app数据里面也有这个transactionId字段,所以在验证收据的时候,让客户端把这个值传过来,这样就可以匹配上是哪个数据了。

坑3:因为支付成功是返回给客户端的,客户端的网络不稳定,有可能不能及时把这个状态发给服务端,可能会导致用户付钱了,但是后台迟迟没有收到信息,导致后续的操作没办法进行。解决:客户端把数据保存在本地,状态为未验证,用户下次启动的时候,把这个数据提交给服务端,走完整个流程。

整个苹果支付的流程图
IAPpng

服务端把客户端提交上来的收据保存在数据库,如果验证失败,可以搞个定时任务继续做验证。为什么会失败?1、调用苹果网络异常;2、苹果服务不可用;3、调用苹果验证超时;4、我们调用苹果出现bug;

当时还担心线上环境和沙盒环境返回的数据不一样,上线后支付了一笔,发现是一样的。

以下是一些伪代码,如果要用要稍微调整下。
验证收据方法


private static final String URL_SANDBOX = "https://sandbox.itunes.apple.com/verifyReceipt";
private static final String URL_VERIFY = "https://buy.itunes.apple.com/verifyReceipt";

1、使用redis对并发进行处理
String key = orderNo的key 全局唯一;
if (rt.opsForValue().setIfAbsent(key, ip地址)) {
	rt.expire(key, 5, TimeUnit.SECONDS);
	//2、检验订单是否存在

	//3、修改订单状态为付款中

	//4、拼成固定的格式传给平台
	Map<String, Object> receiptDataMap = new HashMap<String, Object>(1);
	receiptDataMap.put("receipt-data", ios传上来的base64的数据);
	//5、先线上测试    发送平台验证
	String verifyResult = HttpRequest.postJson(URL_VERIFY, receiptDataMap, null);
	//可以把ios返回的数据保存到订单表里
	if (verifyResult == null) {
		// 苹果服务器没有返回验证结果
		throw new Exception(”订单不存在“);
	} else {
		// 苹果验证有返回结果
		log.info("【ios内购-notifyForPay】正式环境,苹果返回数据:[{}]", verifyResult);
		JSONObject job = JSONObject.parseObject(verifyResult);
		String states = job.getString("status");

		//是沙盒环境,应沙盒测试,否则执行下面 是否开启沙盒环境测试
		String openSandbox = 后台搞个参数管理,进行配置;
		if (Boolean.valueOf(openSandbox)) {
			if ("21007".equals(states)) {
				//再沙盒测试  发送平台验证
				verifyResult = HttpRequest.postJson(URL_SANDBOX, receiptDataMap, null);
				log.info("【ios内购-notifyForPay】沙盒环境,苹果返回数据:[{}]", verifyResult);
				//可以把ios返回的数据保存到订单表里
				job = JSONObject.parseObject(verifyResult);
				states = job.getString("status");
			}
		}

		log.info("【ios内购-notifyForPay】苹果平台返回值:states=[{}]", states);
		// 前端所提供的收据是有效的 验证成功
		if ("0".equals(states)) {
			String receiptJson = job.getString("receipt");
			JSONObject returnJson = JSONObject.parseObject(receiptJson);
			String inApp = returnJson.getString("in_app");
			JSONObject inAppJson = getInApp(JSONObject.parseArray(inApp, JSONObject.class),
					generateOrderDto.getTransactionId());

			String productId = inAppJson.getString("product_id");
			//检查产品包id在我们系统是否可以查到

			// 订单号   transaction_id
			String originalTransactionId = inAppJson.getString("original_transaction_id");

			BigDecimal price = 我们系统的价格;
			boolean isPriceEquals = !(订单表的金额.compareTo(price) == 0);
			//判断金额是否相等
			if (isPriceEquals) {
				//验证平台与ios平台金额是否一致
				//保存错误订单
			} else {
				//保存到数据库 修改订单状态为成功

			}
		} else {
			if (!StringUtils.equals("21005", states)) {
				//修改订单状态为失败

			}
		}
	}
	return 返回值;
}else{
	throw new Exception(“请求太频繁”);
}

工具类

private JSONObject getInApp(List<JSONObject> inAppList,String transactionId){
  for(JSONObject jsonObject : inAppList){
	  String iosTransactionId = jsonObject.getString("transaction_id");
	  if(transactionId.equals(iosTransactionId)){
		  return jsonObject;
	  }
  }
  throw new Exception(“异常);
}

苹果收据返回的数据格式:

{
    "status": 0,
    "environment": "Sandbox",
    "receipt": {
        "receipt_type": "ProductionSandbox",
        "adam_id": 0,
        "app_item_id": 0,
        "bundle_id": "xxxx",
        "application_version": "0",
        "download_id": 0,
        "version_external_identifier": 0,
        "receipt_creation_date": "2018-10-05 10:06:12 Etc/GMT",
        "receipt_creation_date_ms": "1515146772000",
        "receipt_creation_date_pst": "2018-10-05 02:06:12 America/Los_Angeles",
        "request_date": "2018-10-05 10:06:14 Etc/GMT",
        "request_date_ms": "1515146774645",
        "request_date_pst": "2018-10-05 02:06:14 America/Los_Angeles",
        "original_purchase_date": "2013-08-10 07:00:00 Etc/GMT",
        "original_purchase_date_ms": "1375340400000",
        "original_purchase_date_pst": "2013-08-10 00:00:00 America/Los_Angeles",
        "original_application_version": "1.0",
        "in_app": [
            {
                "quantity": "1",
                "product_id": "xxxxxx",
                "transaction_id": "1000000364151455",
                "original_transaction_id": "1000000364151455",
                "purchase_date": "2018-10-05 10:06:11 Etc/GMT",
                "purchase_date_ms": "1515146771000",
                "purchase_date_pst": "2018-10-05 02:06:11 America/Los_Angeles",
                "original_purchase_date": "2018-10-05 10:06:11 Etc/GMT",
                "original_purchase_date_ms": "1515146771000",
                "original_purchase_date_pst": "2018-10-05 02:06:11 America/Los_Angeles",
                "is_trial_period": "false"
            }
        ]
    }
}

关注公众号,更快获取文章

本文为博主原创文章,未经博主允许不得转载。