略微加速

略速 - 互联网笔记

IOS苹果内购 PHP后端验证票据

2019-07-31 leiting (4497阅读)

标签 PHP

大体流程:


1.IOS端需要在iTunes Connect上面添加配置一些内购商品,并审核通过,每个内购商品有自己的唯一标识product_id。


2.PHP后端要有一套与之对应的内购商品、IOS应用唯一标识bundle_id和应用秘钥配置,以便用于验证。如下在tp5的config中做了配置:

//IOS内购配置
'applepay'         =>[
    'bundle_id'     => 'com.oyhdo.lws', //应用唯一标识
    'products'      => ['com.lws.good1','com.lws.good2','com.lws.good3'], //内购商品
    'apple_secret'  => '6666666' //应用密钥,在itunes中获取
],


3.IOS端调起内购支付,支付成功后会返回票据receipt_data,IOS端需将此票据及其他参数提交给后端接口验证(建议加密)。


(后端接收到的receipt_data票据信息打印如下:)

array(3) {
  ["receipt"] => array(18) {
    ["receipt_type"] => string(17) "ProductionSandbox"
    ["adam_id"] => int(0)
    ["app_item_id"] => int(0)
    ["bundle_id"] => string(18) "com.oyhdo.lws"
    ["application_version"] => string(1) "1"
    ["download_id"] => int(0)
    ["version_external_identifier"] => int(0)
    ["receipt_creation_date"] => string(27) "2019-01-16 13:24:02 Etc/GMT"
    ["receipt_creation_date_ms"] => string(13) "1547645042000"
    ["receipt_creation_date_pst"] => string(39) "2019-01-16 05:24:02 America/Los_Angeles"
    ["request_date"] => string(27) "2019-01-16 13:26:00 Etc/GMT"
    ["request_date_ms"] => string(13) "1547645160103"
    ["request_date_pst"] => string(39) "2019-01-16 05:26:00 America/Los_Angeles"
    ["original_purchase_date"] => string(27) "2013-08-01 07:00:00 Etc/GMT"
    ["original_purchase_date_ms"] => string(13) "1375340400000"
    ["original_purchase_date_pst"] => string(39) "2013-08-01 00:00:00 America/Los_Angeles"
    ["original_application_version"] => string(3) "1.0"
    ["in_app"] => array(1) {
      [0] => array(11) {
        ["quantity"] => string(1) "1"
        ["product_id"] => string(15) "com.lws.good1"
        ["transaction_id"] => string(16) "1000000494954657"
        ["original_transaction_id"] => string(16) "1000000494954657"
        ["purchase_date"] => string(27) "2019-01-16 13:24:02 Etc/GMT"
        ["purchase_date_ms"] => string(13) "1547645042000"
        ["purchase_date_pst"] => string(39) "2019-01-16 05:24:02 America/Los_Angeles"
        ["original_purchase_date"] => string(27) "2019-01-16 13:24:02 Etc/GMT"
        ["original_purchase_date_ms"] => string(13) "1547645042000"
        ["original_purchase_date_pst"] => string(39) "2019-01-16 05:24:02 America/Los_Angeles"
        ["is_trial_period"] => string(5) "false"
      }
    }
  }
  ["status"] => int(0)
  ["environment"] => string(7) "Sandbox"
}


4.PHP后端对票据进行验证,并执行一些业务逻辑。代码如下:

//IOS内购
public function applepay(){
    $arr = ['code'=>0,'msg'=>'支付失败','data'=>[]];
    $user_id = $_POST['user_id'];//用户id
    $money = $_POST['money']; //支付金额
    $receipt_data = $_POST['receipt_data'];//票据
    if ($user_id && $money>0 && $receipt_data) {
        //生成商家订单号
        $order_no = 'IOS'.time().rand(10000,99999);
        /**
        添加订单记录
            省略逻辑......
        **/
        if($order_log){
            //验证收据
            $res = $this->validate_applepay($receipt_data,false); //false沙盒 true正式
            if(intval($res['status'])==0) {  //验证成功
                $bundleId = config('bundle_id');
                $products = config('products');
                $bundle_id = $res['receipt']['bundle_id'];
                $product_id = $res['receipt']['in_app'][0]['product_id'];
                if($bundle_id==$bundleId && in_array($product_id,$products)){
                    //更新订单记录
                    $data['trade_no'] = $res['receipt']['in_app'][0]['transaction_id'];//内购交易订单号
                    $data['total_fee'] = $money; //总金额
                    $data['pay_time'] = time(); //支付成功时间
                    $data['state'] = 1;//修改订单状态
                    $updatelog = PayModel::updateLog($order_no,$data);
                    if($updatelog){
                        $arr['code'] = 200;
                        $arr['msg'] = '支付成功';
                    }
                }else{
                    $arr['msg'] = '配置错误';
                }
            }else{  //验证失败
                $arr['code'] = $res['status'];
            }
        }
    }else{
        $arr['msg'] = '参数错误';
    }
    echo json_encode($arr);
}
 
/**
 * IOS内购验证票据
 * @param  string $receipt_data 付款后凭证
 * @return array                验证是否成功
 */
private function validate_applepay($receipt_data,$sandbox=false){
    $apple_secret = config('applepay.apple_secret');
    $jsonData = array('receipt-data'=>$receipt_data,'password'=>$apple_secret);
    $post_json = json_encode($jsonData);
    if($sandbox){
    	$url="https://buy.itunes.apple.com/verifyReceipt";//正式环境
    }else{
    	$url="https://sandbox.itunes.apple.com/verifyReceipt";//沙盒环境
    }
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $post_json);
    $result = curl_exec($ch);
    curl_close($ch);
    return json_decode($result,true);
 
	//返回status示例:
	// * 0     验证成功
	// * 21000 App Store不能读取你提供的JSON对象
	// * 21002 receipt-data域的数据有问题
	// * 21003 receipt无法通过验证
	// * 21004 提供的shared secret不匹配你账号中的shared secret
	// * 21005 receipt服务器当前不可用
	// * 21006 receipt合法,但是订阅已过期。服务器接收到这个状态码时,receipt数据仍然会解码并一起发送
	// * 21007 receipt是Sandbox receipt,但却发送至生产系统的验证服务
	// * 21008 receipt是生产receipt,但却发送至Sandbox环境的验证服务
}


https://blog.csdn.net/msllws/article/details/86515824


北京半月雨文化科技有限公司.版权所有 京ICP备12026184号-3