ApplePayment.php 5.09 KB
<?php
/**
 * Created by PhpStorm.
 * User: lishuai
 * Date: 2022/2/15
 * Time: 4:23 PM
 */

namespace App\Payment;

use App\Models\MembershipGood;
use App\Models\Order;
use App\Models\User;
use App\Models\UserProfile;
use Carbon\Carbon;
use Firebase\JWT\SignatureInvalidException;
use GuzzleHttp\Client;
use Illuminate\Support\Facades\Log;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use Illuminate\Support\Facades\File;

class ApplePayment implements PaymentInterface
{

    const IS_SANDBOX = true;

    const CA_PATH = "/AppleRootCA-G3.pem";

    public function __construct()
    {

    }



    public function prepare(Order $order)
    {
        // 查询订单对应的产品id
    }

    public function notify($all)
    {
        Log::debug(print_r($all,true));

        Log::debug('apple返回的数据:====================');
        $data = json_decode(base64_decode($all['signedPayload']),true);
        Log::debug(print_r($data,true));
    }

    public function notifySandbox($string)
    {
        Log::debug('sandbox返回的数据:====================');

        $components = explode('.',$string);
        if (count($components) < 3){
            Log::error("jwt错误");
            return false;
        }

        $header = json_decode(base64_decode($components[0]),true);

        // 这一步可以省略,不需要验证根证书
        $this->validateAppleRootCa($header);
        $responseBodyPayload = $this->decodeCertificate($string, $header['x5c'][0]);
        Log::debug(print_r($responseBodyPayload,true));
        /**{
            "notificationType": "SUBSCRIBED"
            "subtype": "RESUBSCRIBE"
            "notificationUUID": "99e65e59-c178-4f49-8b83-ea7d916cb568"
            "data": {
                "bundleId": "ink.parlando.parlando"
                "bundleVersion": "13"
                "environment": "Sandbox"
                "signedTransactionInfo": "xxx"
                "signedRenewalInfo": "xxx"
            }
            "version": "2.0"
            "signedDate": 1671451705697
            }
         */

        $signedTransactionInfoString = $responseBodyPayload['data']['signedTransactionInfo'];
        $components = explode('.',$signedTransactionInfoString);
        $header = json_decode(base64_decode($components[0]),true);
        if (count($components) < 3){
            Log::error("jwt错误");
            return false;
        }
        $signedTransactionInfo = $this->decodeCertificate($string, $header['x5c'][0]);
        Log::debug(print_r($signedTransactionInfo,true));
        /**{
            "transactionId": "2000000231419425"
            "originalTransactionId": "2000000229164150"
            "webOrderLineItemId": "2000000017115109"
            "bundleId": "ink.parlando.parlando"
            "productId": "monthly_yiyan_vip"
            "subscriptionGroupIdentifier": "21080623"
            "purchaseDate": 1671451694000
            "originalPurchaseDate": 1671123372000
            "expiresDate": 1671451994000
            "quantity": 1
            "type": "Auto-Renewable Subscription"
            "inAppOwnershipType": "PURCHASED"
            "signedDate": 1671451705700
            "environment": "Sandbox"
          }*/
        $signedRenewalInfoString = $responseBodyPayload['data']['signedRenewalInfo'];
        $components = explode('.',$signedRenewalInfoString);
        $header = json_decode(base64_decode($components[0]),true);
        if (count($components) < 3){
            Log::error("jwt错误");
            return false;
        }
        $signedRenewalInfo = $this->decodeCertificate($string, $header['x5c'][0]);
        Log::debug(print_r($signedRenewalInfo,true));
        /**{
            "originalTransactionId": "2000000229164150"
            "autoRenewProductId": "monthly_yiyan_vip"
            "productId": "monthly_yiyan_vip"
            "autoRenewStatus": 1
            "signedDate": 1671451705673
            "environment": "Sandbox"
            "recentSubscriptionStartDate": 1671451694000
        }*/
    }

    private function validateAppleRootCa($header)
    {
        $lastIndex = count($header['x5c']) - 1;

        $certificate = $this->getCertificate($header['x5c'][$lastIndex]);

        if ($certificate != File::get(public_path(self::CA_PATH))) return false;

        return true;
    }

    private function getCertificate($string)
    {
        $certificate = "-----BEGIN CERTIFICATE-----" . PHP_EOL;
        $certificate .= chunk_split($string, 64, PHP_EOL);
        $certificate .= "-----END CERTIFICATE-----" . PHP_EOL;
        return $certificate;
    }

    private function decodeCertificate($string, $appleCertificate)
    {
        $certificate = $this->getCertificate($appleCertificate);

        $cert_object = openssl_x509_read($certificate);
        $pkey_object = openssl_pkey_get_public($cert_object);
        $pkey_array = openssl_pkey_get_details($pkey_object);
        $public_key = $pkey_array["key"];

        try{
            $decode = JWT::decode($string, new Key($public_key, "ES256"));
            return (array)$decode;
        }catch (SignatureInvalidException $exception){
            Log::error("Signature Invalid!");
            return false;
        }
    }
}