支付时拿到票据:
<?php
/**
* POST验单curl
* @param $post_data 请求参数['key'=>'value','keys'=>'values']
* @param $url 请求地址
* @return mixed
*/
public function post_receipt_data($post_data,$url){
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($curl, CURLOPT_POST, 1);
$post_data = $post_data ? json_encode($post_data) : '';
curl_setopt($curl, CURLOPT_POSTFIELDS, $post_data);
if( !empty($aHeader) ){
curl_setopt($curl, CURLOPT_HTTPHEADER, $aHeader);
}
curl_setopt($curl, CURLOPT_TIMEOUT, 120);
curl_setopt($curl, CURLOPT_HEADER, 0);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$result = curl_exec($curl);
$info = curl_getinfo($curl);
$error_no = curl_errno($curl);
$error_str = curl_error($curl);
curl_close($curl);
$result_array = json_decode($result,true);
return $result_array;
}
/**
* 苹果验单转发方法
*
* apple地址:
* https://developer.apple.com/library/archive/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html
*
* @param $receipt 苹果票据
* @param int $type 1:普通充值验单 2:支持传共享秘钥验单(不传则默认取订阅配置的秘钥)
* @param null $url 请求地址
* @param null $pwd 共享秘钥
* @return mixed
*/
public function apple_validation_url($receipt,$type=1, $url=null, $pwd = null)
{
$post_data['receipt-data'] = $receipt; //验单票据
//增加共享秘钥参数
if($type === 2){
$post_data['password'] = !empty($pwd) ? $pwd :$this->my_config['apple_shared_secret_key'];
$post_data['exclude-old-transactions'] = true; //true仅返回最新订阅信息
}
$url = !empty($url) ? $url : "https://buy.itunes.apple.com/verifyReceipt"; //正式地址
$test_verify_url = "https://sandbox.itunes.apple.com/verifyReceipt"; //测试环境地址
$prod_verify_url = "https://buy.itunes.apple.com/verifyReceipt"; //生产环境地址
$result_validation_info = $this->post_receipt_data($post_data, $url);
switch ($result_validation_info['status']) {
case 0: //正常
break;
case 21006: //订阅到期返回过期类票据
break;
case 21007: //沙盒模式的票据发送给了生产环境-重新转发给到测试地址
$result_validation_info = $this->post_receipt_data($post_data, $test_verify_url);
break;
case 21008: //生产模式的票据发送给了测试环境-重新转发给到正式地址
$result_validation_info = $this->post_receipt_data($post_data, $prod_verify_url);
break;
default: //记录错误日志
//todo 写log弹出错误;
\Common::response_error_header(403,"verify fail");
}
return $result_validation_info;
}
/**
* 苹果验单数据解析(订阅类型返回结构体)
* @param array $data 数据
* @param int $type 结构体类型 1普通支付验单结构体 2订阅首次支付验单票据返回结构体 3apple订阅回调票据返回结构体
* @return array
*/
public function apple_data_parsing(array $data = [],$type = 2)
{
if (empty($data)) return $data; //没有数据
if ($data['status'] != 0 && $data['status'] != 21006) return $data; //status不等于0
$k= []; //返回结构体
switch ($type){
case 1: //普通验单结构
$ks['status'] = $data['status'];
$ks['receipt'] = $data['receipt'];
unset($ks['receipt']['in_app']);
$ks['receipt']['in_app'][0] = $data['receipt']['in_app'][0];
//取in_app信息
$k = array_merge($ks,$data['receipt']['in_app'][0]);
break;
case 2:
$k['status'] = $data['status'];
$k['receipt'] = $data['receipt'];
unset($k['receipt']['in_app']);
$k['pending_renewal_info'] = $data['pending_renewal_info'];
//票据
if(isset($data['latest_receipt'])){
$k['latest_receipt'] = $data['latest_receipt'];
}
//如果存在值-降序取最新的数据-兼容返回全部订阅信息
$latest = !empty($data['latest_receipt_info']) ? array_reverse($data['latest_receipt_info']) : [];
//产品id
if (isset($latest[0]['product_id'])) {
$k['product_id'] = $latest[0]['product_id'];
}
//事件id
if (isset($latest[0]['transaction_id'])) {
$k['transaction_id'] = $latest[0]['transaction_id'];
}
//原始事件id
if (isset($latest[0]['original_transaction_id'])) {
$k['original_transaction_id'] = $latest[0]['original_transaction_id'];
}
//到期时间
if (isset($latest[0]['expires_date_ms'])) {
$k['expires_date'] = floor($latest[0]['expires_date_ms'] / 1000); //转换成时间戳 - 到期时间
}
//订阅状态
if (isset($data['pending_renewal_info'][0]['auto_renew_status'])) {
$k['auto_renew_status'] = $data['pending_renewal_info'][0]['auto_renew_status'];
}
break;
case 3:
$k['status'] = $data['status'];
$k['receipt'] = $data['receipt'];
//21006 apple返回到期票据返回结构体
if($data['status'] == 21006 && !empty($data['latest_expired_receipt_info'])){
$latest = $data['latest_expired_receipt_info'];
}else{
$latest = $data['latest_receipt_info'];
}
//票据
if(isset($data['latest_receipt'])){
$k['latest_receipt'] = $data['latest_receipt'];
}
//产品id
if (isset($data['auto_renew_product_id']['product_id'])) {
$k['product_id'] = $data['auto_renew_product_id']['product_id'];
}
//事件id
if (isset($latest['transaction_id'])) {
$k['transaction_id'] = $latest['transaction_id'];
}
//原始事件id
if (isset($latest['original_transaction_id'])) {
$k['original_transaction_id'] = $latest['original_transaction_id'];
}
//到期时间
if (isset($latest['expires_date'])) {
$k['expires_date'] = floor($latest['expires_date'] / 1000); //转换成时间戳 - 到期时间
}
//订阅状态
if (isset($data['auto_renew_status'])) {
$k['auto_renew_status'] = $data['auto_renew_status'];
}
break;
default:
return $data;
}
return $k;
}
?>
回调方法:
/**
* ios订阅回调
* ios_subscribe_callback
* @annotation 同一个苹果appleID 购买的同一个商品的时 original_transaction_id 从始至终都是相同的
*/
public function iosSubscribeCallback()
{
$row_data = file_get_contents('php://input');
if($row_data){
$data = json_decode($row_data,true);
switch ($data){
case !empty($data['latest_receipt']): //最新票据
$latest_receipt = $data['latest_receipt'];
break;
case !empty($data['latest_expired_receipt']): //最新过期票据
$latest_receipt = $data['latest_expired_receipt'];
break;
default:
$latest_receipt = $data['latest_receipt'];
}
if(!empty($latest_receipt)){
//使用票据去苹果验单
$receipt_info = $this->apple_validation_url($latest_receipt,2);
$paper_a = $this->apple_data_parsing($receipt_info,3);
//返回状态0 并且到期时间大于当前时间
if($paper_a['status']==0 || $paper_a['status']==21006){
//根据原始事件id查询没有关闭订阅服务的追她用户信息进行延期等操作
//到期时间大于存储的到期时间
if($paper_a['expires_date'] > $y['end_time'] && $paper_a['auto_renew_status'] == 1){
//用户续费成功
//todo 逻辑处理
}else{
//用户取消订阅状态
if($paper_a['auto_renew_status']== 0){
//todo逻辑处理
}
}
}
}else{
\Common::response_error_header(403,'Invalid latest_receipt');
}
}else{
//无效数据
}
\Common::response_success_header(200);
exit;
}
注意:回调时候比较坑的是他放票据的键名不是一样的。要对应取