苹果IAP内购支付接口遇到的一些坑

苹果IAP内购支付接口遇到的一些坑

11 May 2017

第一次对接Apple内购,几乎把坑都踩了一遍,大公司就是傲娇,尼玛稍不注意就漏洞一大堆。

 

一、关于沙盒

苹果还算有点良心,提供了沙河环境,不然为了测试这个劳什子玩意儿不知道要搭进去多少钱╮(╯_╰)╭

提交给App Store审核时,apple审核人员也是在沙盒下测试的,如果这个时候用的正式环境,绝壁会被毙掉╮(╯_╰)╭

 

二、关于回调

苹果相当懒,支付成功后,是不会主动去回调我们的,所以需要在app端直接通知自己的服务器支付是否成功,然后服务端决定是否发放商品

 

三、订单验证

由于是通过app端直接通知服务器,切记一定要把订单验证放在服务端,不然分分钟被破解

 

四、验证返回数据

订单验证时,低版本iOS返回的数据格式是不一样的,这个需要做判断处理(苹果你大爷的╮(╯_╰)╭)

 

五、订单合法检测

如果你以为receipt验证通过了就万事大吉,那就图样了╮(╯_╰)╭,苹果只是老老实实的返回receipt凭证到底有没有支付成功,其他的人家是不管的,如果不做合法检测,分分钟被刷单的搞死。。。建议做以下检测:

  1. 比对bundle_id参数,这个表示app的厂家,格式为com.xxx.xxx,看看这个订单是不是自家的
  2. 检查充值金额,比如20块的id为gold20,我们拿这个去跟数据库中的订单比对
  3. 订单重复检测,每完成一笔充值,要把transaction_id记录下来,每次都要查一下这个参数是否已存在,否则人家可以拿同一个支付凭证大量的刷单

 

下面附上一段很常用的服务端二次验证代码,订单合法检测的代码根据自己的业务自己去实现:

/**
 * 发起二次验证请求
 * @receipt base64编码后的支付凭证
 * @isSandbox 沙河环境
 * */
private function getReceiptData($receipt, $isSandbox = false) {
	if ($isSandbox) {
		$endpoint = 'https://sandbox.itunes.apple.com/verifyReceipt';
	}
	else {
		$endpoint = 'https://buy.itunes.apple.com/verifyReceipt';
	}

    //此处一定要做处理,别问我为什么,大坑
	$receipt = str_replace(" ", "+", $receipt);
	$postData = '{"receipt-data":"'.$receipt.'"}';

	$header = array();
	$opts = array(
		CURLOPT_TIMEOUT        => 30,
		CURLOPT_RETURNTRANSFER => 1,
		CURLOPT_SSL_VERIFYPEER => false,
		CURLOPT_SSL_VERIFYHOST => false,
		CURLOPT_HTTPHEADER     => $header
	);
	$opts[CURLOPT_URL] = $endpoint;
	$opts[CURLOPT_POST] = 1;
	$opts[CURLOPT_POSTFIELDS] = $postData;
	
	$ch = curl_init();
	curl_setopt_array($ch, $opts);
	$response = curl_exec($ch);
	$errno    = curl_errno($ch);
	$errmsg   = curl_error($ch);
	curl_close($ch);
	
	//判断时候出错,抛出异常
	if ($errno != 0) {
		return false;
	}
	$data = json_decode($response, true);
	//判断返回的数据是否是对象
	if (!$data) {
		return false;
	}
	//判断购买时候成功
	if (!isset($data->status) || $data->status != 0) {
		return array('status'=>$data->status);
	}

	if ($data['receipt']['in_app']) {
		$receiptData = $data['receipt']['in_app'][0];
		$bid = $data['receipt']['bundle_id'];
		$bvrs = $data['receipt']['bvrs'];
	}else {
		$receiptData = $data['receipt'];
		$bid = $data['receipt']['bid'];
		$bvrs = $data['receipt']['application_version'];
	}

	//返回产品的信息
	return array(
		'status'         =>  $receiptData['status'],
		'quantity'       =>  $receiptData['quantity'],
		'product_id'     =>  $receiptData['product_id'],
		'transaction_id' =>  $receiptData['transaction_id'],
		'purchase_date'  =>  $receiptData['purchase_date'],
		'bid'            =>  $bid,
		'bvrs'           =>  $bvrs
	);
}