apple iap pay app store内购支付后端处理+流程图+数据库表设计+php代码

文章目录[隐藏]
WPJAM TOC
苹果内购2.0版本需要添加数据库和代码>

我们需要定义3个接口:

  • 获取商品列表接口(这个商品是在APP store 上的内购商品,自行将对应数据存数据库)

  • 生成服务端订单号接口(便于服务端数据记录和用户数据处理关联等)

  • app请求app store 下单支付后返回的收据验证接口(主要是check 发票,目前就是check 数据是否支付成功,让后修改一些对应用户支付后的数据)

内购支付流程图>

添加或修改

  • 数据库

    • 内购商品表

      CREATE TABLE online_apple_products
      (
          id int PRIMARY KEY COMMENT '自增id' AUTO_INCREMENT,
          product_id varchar(32) DEFAULT '' NOT NULL COMMENT '苹果内购商品id',
          title varchar(64) DEFAULT '' NOT NULL COMMENT '商品标题',
          price double(8,2) DEFAULT 0 NOT NULL COMMENT '商品价格(人民币-元)',
          status tinyint DEFAULT 0 NOT NULL COMMENT '商品状态,默认0  1代表上架商品',
          created_at timestamp COMMENT '创建时间',
          updated_at timestamp COMMENT '修改时间',
      );
      CREATE UNIQUE INDEX online_apple_products_product_id_uindex ON online_apple_products (product_id);
      ALTER TABLE online_apple_products COMMENT = '苹果内购商品表';
      
      
      
    • 内购订单表SQL

      CREATE TABLE online_apple_iap_order
      (
          id bigint(20) PRIMARY KEY COMMENT '订单自增id' AUTO_INCREMENT,
          user_id bigint(20) DEFAULT 0 NOT NULL COMMENT '学生id',
          apple_product_id int DEFAULT 0 NOT NULL COMMENT '苹果商品表apple_product主键ID',
          order_no varchar(32) DEFAULT '' NOT NULL COMMENT '下单生成订单号',
          status tinyint DEFAULT 0 NOT NULL COMMENT '订单状态:-1=支付失败 0=默认下单,1=支付成功',
          pay_time timestamp COMMENT '支付成功时间',
          price double(8,2) DEFAULT 0 NOT NULL COMMENT '支付金额',
          product_id varchar(32) DEFAULT '' COMMENT '苹果app store 商品ID',
          request_data text COMMENT 'Aapple app返回服务端请求数据体',
          response_data text COMMENT 'check苹果服务器返回数据结构体',
          created_at timestamp COMMENT '创建时间',
          updated_at timestamp COMMENT '更新时间',
          deleted_at timestamp COMMENT '软删除时间'
      );
      CREATE UNIQUE INDEX online_apple_iap_order_order_no_uindex ON online_apple_iap_order (order_no);
      ALTER TABLE online_apple_iap_order COMMENT = '苹果内购订单表';
      • 核心check apple server 代码,其他逻辑自行搞定

      <?php
      /**
       * Created by PhpStorm.
       * User: shaozeming
       * Date: 2019/4/21
       * Time: 6:49 PM
       */
      
      namespace App\Services\Student\Order\AppleIAPPay;
      
      use GuzzleHttp\Exception\ClientException;
      use GuzzleHttp\Client;
      
      class AppleIAPService
      {
      
      
          //版本号
          const APP_VERSION = '1.0.1';
          /**
           * 沙盒请求地址
           */
          const SANDBOX_URL = 'https://sandbox.itunes.apple.com/verifyReceipt';
          /**
           * 生产环境地址
           */
          const PRODUCTION_URL = 'https://buy.itunes.apple.com/verifyReceipt';
          /**
           * app客户端给予的数据
           * @var
           */
          private $receiptData;
          /**
           * 密码
           * @var string
           */
          private $password = '';
          /**
           * 是否使用沙盒模式
           * @var bool
           */
          private $sandbox = true;
          /**
           * 最终请求url
           * @var string
           */
          private $requestUrl;
      
          /**
           * 错误状态码数据
           * @var array
           */
          static $errorMsg = [
              21000 => "App Store无法读取你提供的JSON数据",
              21002 => "收据数据不符合格式",
              21003 => "收据无法被验证",
              21004 => "你提供的共享密钥和账户的共享密钥不一致",
              21005 => "收据服务器当前不可用",
              21006 => "收据是有效的,但订阅服务已经过期。当收到这个信息时,解码后的收据信息也包含在返回内容中",
              21007 => "收据信息是测试用(sandbox),但却被发送到产品环境中验证",
              21008 => "收据信息是产品环境中使用,但却被发送到测试环境中验证",
              21010 => "收据不合法",
              'error' => "内部数据访问错误",
          ];
      
      
          /**
           * 构造方法
           * IAPService constructor.
           * @param $receiptData
           * @param $password
           * @param $sandbox
           */
          public function __construct($receiptData, $password, $sandbox)
          {
              $this->receiptData = $receiptData;
              $this->password = $password;
              $this->sandbox = $sandbox;
              if ($this->sandbox === true) {
                  $this->requestUrl = self::SANDBOX_URL;
              } else {
                  $this->requestUrl = self::PRODUCTION_URL;
              }
          }
      
      
          /**
           * 组合验证请求数据结构
           * User: ZeMing Shao
           * Email: szm19920426@gmail.com
           * @return array
           */
          private function encodeRequest()
          {
              if ($this->password == '') {
                  return ['receipt-data' => $this->receiptData];
              } else {
                  return ['receipt-data' => $this->receiptData, 'password' => $this->password];
              }
          }
      
          /**
           * json 转换
           * User: ZeMing Shao
           * Email: szm19920426@gmail.com
           * @param $response
           * @return mixed
           */
          private function decodeResponse($response)
          {
              return json_decode($response);
          }
      
      
          /**
           * 初始化请求
           * initiate validation request
           * User: ZeMing Shao
           * Email: szm19920426@gmail.com
           * @return string
           * @throws \GuzzleHttp\Exception\GuzzleException
           */
          private function makeRequest()
          {
              $httpRequest = new Client();
              try {
                  $response = $httpRequest->request('POST', $this->requestUrl, [
                      'json' => $this->encodeRequest()
                  ]);
                  return $response->getBody()->getContents();
              } catch (ClientException $e) {
                  return $e->getMessage();
              }
          }
      
      
          /**
           * 效验apple 验证数据
           * User: ZeMing Shao
           * Email: szm19920426@gmail.com
           * @return string
           * @throws \GuzzleHttp\Exception\GuzzleException
           */
          public function validateReceipt()
          {
              $response = $this->makeRequest();
              $decodedResponse = $this->decodeResponse($response);
              return $decodedResponse;
          }
      }
      
      调用代码