首页 > 编程语言 >PHP接入苹果支付

PHP接入苹果支付

时间:2023-02-23 15:23:27浏览次数:45  
标签:purchase transaction 接入 receipt original 苹果 date PHP id

Ios苹果支付流程:

  1. 客户端先从苹果获取内购Id。
  2. 客户端将内购id,金额、用户id等传给服务端获取一个自己服务端生成的订单号。
  3. 客户端向苹果发起支付。
  4. 支付成功后,客户端从本地拿支付凭证、将支付凭证和订单号、用户id等参数传给服务端;服务端拿支付凭证向苹果发起验证订单是否有效,然后将结果反馈给客户端。
  5. 客户端刷新用户个人信息。

代码

/**
 * 服务器二次验证代码
 * @param $receipt
 * @param bool $isSandbox
 * @return array|void
 * @throws \Exception
 */
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 = json_encode(["receipt-data" => $receipt]);
    //$postData = '{"receipt-data":"'. $receipt .'"}';
    BLog::pay("苹果支付:postData:$postData");

    $ch = curl_init($endpoint);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);  //这两行一定要加,不加会报SSL 错误
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);


    $response = curl_exec($ch);
    $errno    = curl_errno($ch);
    $errmsg   = curl_error($ch);
    curl_close($ch);
    
    //判断时候出错,抛出异常
    if ($errno != 0) {
        return [
            'result'     => false,
            'txtMessage' => $errmsg,
        ];
    }

    $data = json_decode($response);

    BLog::pay("苹果支付:response:$response");

    //此处是看到先人们的指导,又看到apple的官方说法改的。否则会审核不过貌似是审核也会走沙盒测试者,
    //此处先判断一次返回的status是否=21007 这数据是从测试环境,但它发送到生产环境中进行验证。它发送到测试环境来代替。
    if ($data->status == 21007) {
        $this->getReceiptData($receipt, true);
        return;
    }
    //判断返回的数据是否是对象
    if (!is_object($data)) {
        throw new \Exception('Invalid response data');
    }
    //判断购买时候成功
    if (!isset($data->status) || $data->status != 0) {
        throw new \Exception('Invalid receipt');
    }

    //返回产品的信息
    return (array)$data->receipt;
}

/**
 * ios 支付验证
 * @param $allData
 * @return array|string
 */
public function iosIAPPay($allData) {
    $receiptData = $allData['receiptData'];
    //获取 App 发送过来的数据,设置时候是沙盒状态
    $receipt   = $receiptData;
    $isSandbox = true;
    if ('server' == APP_ENV) {
        $isSandbox = false;
    }
    //开始执行验证
    try {
        $info = $this->getReceiptData($receipt, $isSandbox);

        $package_name = $info['bid'] ?? "";
        if (!$package_name) {
            $package_name = $info['bundle_id'] ?? "";
        }
        if (!$package_name) {
            return [
                'result'     => false,
                'txtMessage' => 'bid或bundle_id缺失',
            ];
        }

        $product_id = $info['product_id'] ?? "";
        if (!$product_id) {
            $product_id = $info['in_app'][0]->product_id ?? "";
        }
        if (!$product_id) {
            return [
                'result'     => false,
                'txtMessage' => 'product_id缺失',
            ];
        }

        $transaction_id = $info['transaction_id'] ?? "";
        if (!$transaction_id) {
            $transaction_id = $info['in_app'][0]->transaction_id ?? "";
        }
        if (!$transaction_id) {
            return [
                'result'     => false,
                'txtMessage' => 'transaction_id缺失',
            ];
        }

        $productInfo = DB::table('ios_payment_config')
                            ->where('package_name', $package_name)
                            ->where('product_id', $product_id)
                            ->first();
        if (!$productInfo) {
            BLog::pay("苹果支付:无该产品:" . json_encode($info));
            return [
                'result'     => false,
                'txtMessage' => '无该产品',
            ];
        }
        $user_open_id = $info['download_id'] ?? "";
        $res          = $this->doPay($allData['userId'], $allData['orderId'], $transaction_id, $user_open_id);
        BLog::pay("苹果支付:支付结果:" . json_encode($info) . json_encode($res));
        return $res;

    } catch (\Exception $e) {
        return [
            'result'     => false,
            'txtMessage' => $e->getMessage(),
        ];
    }
}

常见错误码

/**
* 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": {
        "receipt_type": "ProductionSandbox",
        "adam_id": 0,
        "app_item_id": 0,
        "bundle_id": "申请苹果支付时的串号 固定的值",
        "application_version": "24",
        "download_id": 0,
        "version_external_identifier": 0,
        "receipt_creation_date": "2022-************Etc/GMT",
        "receipt_creation_date_ms": "1643************000",
        "receipt_creation_date_pst": "2022-0a************/Los_Angeles",
        "request_date": "2022-02-2************Etc/GMT",
        "request_date_ms": "164************7",
        "request_date_pst": "2022-************ngeles",
        "original_purchase_date": "201************tc/GMT",
        "original_purchase_date_ms": "13************00",
        "original_purchase_date_pst": "2013************geles",
        "original_application_version": "1.0",
        "in_app": [ //变成了数组
            {
                "quantity": "1",
                "product_id": "10000",#产品ID
                "transaction_id": "", ##交易单号
                "original_transaction_id": "",
                "purchase_date": "2022************Etc/GMT",
                "purchase_date_ms": "164************0",
                "purchase_date_pst": "2022-************les",
                "original_purchase_date": "2022************GMT",
                "original_purchase_date_ms": "164************0",
                "original_purchase_date_pst": "202************les",
                "is_trial_period": "false",
                "in_app_ownership_type": "PURCHASED"#交易状态
            },
            {
                "quantity": "1",
                "product_id": "10000",#产品ID
                "transaction_id": "", ##交易单号
                "original_transaction_id": "",
                "purchase_date": "2022************Etc/GMT",
                "purchase_date_ms": "164************0",
                "purchase_date_pst": "2022-************les",
                "original_purchase_date": "2022************GMT",
                "original_purchase_date_ms": "164************0",
                "original_purchase_date_pst": "202************les",
                "is_trial_period": "false",
                "in_app_ownership_type": "PURCHASED"#交易状态
            },
        ]
    },
    "environment": "Sandbox", //环境
    "status": 0
}

老版本是一个一维数组,没有in_app

post请求的坑,报21003

  • 第一种,有空格,使用字符串替换
$receipt = str_replace(' ',"+", $receipt);
  • 第二种,json不对,直接拼字符串,不要json_encode
$postData = '{"receipt-data":"'. $receipt .'"}';
  • 第三种,苹果那边的问题,什么都没改,第一天好的,第二天报错(沙盒报错,线上好的),第三天好了。

标签:purchase,transaction,接入,receipt,original,苹果,date,PHP,id
From: https://www.cnblogs.com/HappyTeemo/p/17148081.html

相关文章

  • 苹果14怎么设置几天后的闹钟提醒?
    2022年的苹果14系列手机一经发布,就受到了不少消费者的青睐。不过有一些用户在使用苹果14手机中的过程中遇到了一些问题,这就是自己在三天后有一个重要的会议需要参见,因为担......
  • node.js实现国标GB28181设备接入sip服务器解决方案SkeyeVSS国标视频云平台
    GB28181接入服务器是SkeyeVSS接入GB28181设备/平台的信令交互服务器,GB28181将SIP定位为联网系统的主要信令基础协议,并利用SIP协议的有关扩展,实现了对非会话业务的兼顾,例如......
  • 网页支付宝支付,通过form表单提交,在苹果手机上无法跳转
    1.首先判读是否为ios系统varu=navigator.userAgent;varisAndroid=u.indexOf('Android')>-1||u.indexOf('Adr')>-1;//android终端varisiOS=!!u.match(/\(i......
  • [代码审计基础 15]phpmcs_v9.6 rce
    PHP:5.4.5设置调试:https://blog.csdn.net/m0_46641521/article/details/120107786版本:phpcms_v9.6.0_UTF80x01:路由分析和前一篇一样的路由,但是对输入的关键字都有了防护......
  • PHP DateTime 对象和 Date 函数的 Demo
    DateTime对象<?php//设置时间时区date_default_timezone_set('PRC');$dateFormat="Y-m-d";$dateTimeFormat="Y-m-dH:i:s";//获取当前时间$date=newDateTi......
  • php-mysql 函数练习和使用!
    1.回顾:上篇学习和使用了wamp+eclipse的环境搭建2.这篇将学习一个例子,使用mysql函数来学习3.基本操作  3.1设置编码header("Content-type:text/html;charset=utf-8......
  • php-wamp+eclipse+php环境搭建
    1.回顾:上篇已经解决了wamp与IIS冲突的问题2.这篇就开始使用wamp+eclipse搭建php的开发环境3.准备篇  3.1下载wamp软件      在百度里可以搜索到,安装的......
  • php-IIS和WampServer 冲突问题
    1.问题ForbiddenYoudon'thavepermissiontoaccess/onthisserver.2.解决方案右击红色图标>localhost,我们会发现跳转的网页是,这个是IIS的网页,这就说明WampSever与IIS......
  • phpstorm go py 30天无线试用
    1.关闭软件(建议先导出设置配置,操作会重置配置)2.删除文件(一般AppData会被隐藏,需要通过查看选项里面打开查看隐藏文件,不懂自行百度)删除目录 C:\Users\用户名\AppData\Roam......
  • 联想小新 Air-14 2019IML电脑 Hackintosh 黑苹果efi引导文件
    原文来源于黑果魏叔官网,转载需注明出处。硬件型号驱动情况主板LenovoLNVNB161216处理器IntelCorei5-10210U/i7-10510U已驱动内存8GBDDR42666已驱动硬盘康佳KAK0500B1......