PHP 苹果支付服务器端验证

支付接口 1282 浏览

PHP 苹果支付服务器端验证。

随着苹果取禁止微信直接打赏等功能,许多APP想要在IOS上直接支付就不行了,必须要使用ios的内购才能继续上架。但是往往为了防止伪造信息,我们在服务器端还要进行二次验证才能确保不会出错。下面就是我最近整理的方法。


| 处理业务逻辑

/**
 * 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环境的验证服务
 *
 * $receipt  苹果返回的支付凭证,ios客户端base64加密后的长字符串
 * $sandbox  为1时$url为测试地址,为0时为正试地址
 * $orderid  苹果订单号
 * $pro_id   支付产品的id,以产品形式获取充值的金额
 */
public function appleVerify(){
    if(request()->isPost()){
        $post    = input('param.');
        $receipt = $post['receipt'];
        $sandbox = $post['sandbox'];
        $orderid = $post['transaction_id'];
        $pro_id  = $post['product_id'];
        try{
            // 创建订单
            $order = new Bases('order');
            $info  = $order->find(['transaction_id'=>$orderid]);
            if($info){
                throw new \LogicException('订单已存在',1001);
            }

            /**
             * 获取产品信息
             * 苹果支付不直接传金额,以产品形式获取充值的金额
             *  我们需要在自己服务器根据产品id取出产品价格
             */
            $product = new Bases('product');
            $product_info = $product->find(['id'=>$pro_id]);

            // 添加充值记录
            $add = [
                'uid'        => 'uid',
                'money'      => $product_info['money'],
                'pay_method' => 'ios',
                'transaction_id' => $orderid,
            ];
            $bool = $order->insert($add);
            if(!$bool){
                throw new \LogicException('操作失败',1002);
            }

            // 去苹果进行二次验证,防止收到的是伪造的数据
            $result = $this->appleReceipt($receipt,$sandbox);
            if(!is_array($result)){
                throw new \LogicException('操作失败',1002);
            }
            
            //没有错误就进行业务逻辑的处理,订单设置成已支付,给用户加钱
            $save = [
                'pay_time' => time(),
                'status'   => 'TRADE_FINISHED',
            ];
            
            // 修改充值订单状态
            $bool1 = $recharge->updateData($save,['transaction_id'=>$result['transaction_id']]);
            if(!$bool1){
                throw new \LogicException('操作失败',1002);
            }

            // 账户加钱
            $account = new Bases('account');
            $bool2 = $account->_setInc(['uid'=>$info['uid']],'money',$product_info['money']);
            if(!$bool3){
                throw new \LogicException('操作失败',1002);
            }
        } catch (\Exception $e){
            return json_encode(['code'=>$e->getCode(),'msg'=>$e->getMessage()]);
        }
        return json_encode(['code'=>0,'msg'=>'充值成功']);
    }
}

| 去苹果服务器进行二次验证

/**
 * 去苹果服务器二次验证,参数是从上面业务逻辑处传递过来的
 * @param $receipt 苹果返回的支付凭证,ios客户端base64加密后的长字符串
 * @param $sandbox 为1时$url为测试地址,为0时为正试地址
 * @return array
 */
protected function appleReceipt($receipt,$sandbox){
    // 必须以该格式发送给苹果服务器
    $postData = json_encode(
        array('receipt-data' => $receipt)
    );
    // url_buy正式购买地址 url_sandbox沙盒购买地址
    $url_buy     = "https://buy.itunes.apple.com/verifyReceipt";
    $url_sandbox = "https://sandbox.itunes.apple.com/verifyReceipt";
    $url = $sandbox ? $url_sandbox : $url_buy;

    // curl获取苹果返回的数据
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
    $response = curl_exec($ch);
    $errno    = curl_errno($ch);
    curl_close($ch);
    
    if ($errno != 0) {
            throw new \LogicException('请求超时,请稍后重试',1033);
        }
        /**
         * $data['status']==0  成功
         * $data['receipt']['in_app'][0]['transaction_id']  苹果订单号
         * $data['receipt']['in_app'][0]['product_id'];  商品价格
         */
        $data = json_decode($response, true);
        if (!is_array($data)) {
            throw new \LogicException('苹果返回数据有误,请稍后重试',1034);
        }
        if (!isset($data['status']) || $data['status'] != 0) {
            throw new \LogicException('购买失败',1035);
        }
        //返回产品的信息
        return $data['receipt']['in_app'][0];
}

这样我们就基本完成苹果支付了,如果遇到21002错误,那绝对是ios那边传过来的加密数据有错误。

ps:我就被我们的ios坑了一下午时间,我反复问过几次苹果传过的来的数据是不是他们给我的,结果还是给错了格式,最后加密出来的数据就不对。

|  版权声明:本文为博主原创文章,转载请注明出处。