李帅

1.打印苹果日志

...@@ -13,16 +13,19 @@ use App\Models\Order; ...@@ -13,16 +13,19 @@ use App\Models\Order;
13 use App\Models\User; 13 use App\Models\User;
14 use App\Models\UserProfile; 14 use App\Models\UserProfile;
15 use Carbon\Carbon; 15 use Carbon\Carbon;
16 +use Firebase\JWT\SignatureInvalidException;
16 use GuzzleHttp\Client; 17 use GuzzleHttp\Client;
17 use Illuminate\Support\Facades\Log; 18 use Illuminate\Support\Facades\Log;
18 -use Illuminate\Support\Facades\Redis; 19 +use Firebase\JWT\JWT;
20 +use Firebase\JWT\Key;
21 +use Illuminate\Support\Facades\File;
19 22
20 class ApplePayment implements PaymentInterface 23 class ApplePayment implements PaymentInterface
21 { 24 {
22 25
23 const IS_SANDBOX = true; 26 const IS_SANDBOX = true;
24 27
25 - 28 + const CA_PATH = "/AppleRootCA-G3.pem";
26 29
27 public function __construct() 30 public function __construct()
28 { 31 {
...@@ -48,7 +51,6 @@ class ApplePayment implements PaymentInterface ...@@ -48,7 +51,6 @@ class ApplePayment implements PaymentInterface
48 public function notifySandbox($string) 51 public function notifySandbox($string)
49 { 52 {
50 Log::debug('sandbox返回的数据:===================='); 53 Log::debug('sandbox返回的数据:====================');
51 - Log::debug($string);
52 54
53 $components = explode('.',$string); 55 $components = explode('.',$string);
54 if (count($components) < 3){ 56 if (count($components) < 3){
...@@ -58,6 +60,105 @@ class ApplePayment implements PaymentInterface ...@@ -58,6 +60,105 @@ class ApplePayment implements PaymentInterface
58 60
59 $header = json_decode(base64_decode($components[0]),true); 61 $header = json_decode(base64_decode($components[0]),true);
60 62
61 - Log::debug(print_r($header,true)); 63 + // 这一步可以省略,不需要验证根证书
64 + $this->validateAppleRootCa($header);
65 + $responseBodyPayload = $this->decodeCertificate($string, $header['x5c'][0]);
66 + Log::debug(print_r($responseBodyPayload,true));
67 + /**{
68 + "notificationType": "SUBSCRIBED"
69 + "subtype": "RESUBSCRIBE"
70 + "notificationUUID": "99e65e59-c178-4f49-8b83-ea7d916cb568"
71 + "data": {
72 + "bundleId": "ink.parlando.parlando"
73 + "bundleVersion": "13"
74 + "environment": "Sandbox"
75 + "signedTransactionInfo": "xxx"
76 + "signedRenewalInfo": "xxx"
77 + }
78 + "version": "2.0"
79 + "signedDate": 1671451705697
80 + }
81 + */
82 +
83 + $signedTransactionInfoString = $responseBodyPayload['data']['signedTransactionInfo'];
84 + $components = explode('.',$signedTransactionInfoString);
85 + $header = json_decode(base64_decode($components[0]),true);
86 + if (count($components) < 3){
87 + Log::error("jwt错误");
88 + return false;
89 + }
90 + $signedTransactionInfo = $this->decodeCertificate($string, $header['x5c'][0]);
91 + Log::debug(print_r($signedTransactionInfo,true));
92 + /**{
93 + "transactionId": "2000000231419425"
94 + "originalTransactionId": "2000000229164150"
95 + "webOrderLineItemId": "2000000017115109"
96 + "bundleId": "ink.parlando.parlando"
97 + "productId": "monthly_yiyan_vip"
98 + "subscriptionGroupIdentifier": "21080623"
99 + "purchaseDate": 1671451694000
100 + "originalPurchaseDate": 1671123372000
101 + "expiresDate": 1671451994000
102 + "quantity": 1
103 + "type": "Auto-Renewable Subscription"
104 + "inAppOwnershipType": "PURCHASED"
105 + "signedDate": 1671451705700
106 + "environment": "Sandbox"
107 + }*/
108 + $signedRenewalInfoString = $responseBodyPayload['data']['signedRenewalInfo'];
109 + $components = explode('.',$signedRenewalInfoString);
110 + $header = json_decode(base64_decode($components[0]),true);
111 + if (count($components) < 3){
112 + Log::error("jwt错误");
113 + return false;
114 + }
115 + $signedRenewalInfo = $this->decodeCertificate($string, $header['x5c'][0]);
116 + Log::debug(print_r($signedRenewalInfo,true));
117 + /**{
118 + "originalTransactionId": "2000000229164150"
119 + "autoRenewProductId": "monthly_yiyan_vip"
120 + "productId": "monthly_yiyan_vip"
121 + "autoRenewStatus": 1
122 + "signedDate": 1671451705673
123 + "environment": "Sandbox"
124 + "recentSubscriptionStartDate": 1671451694000
125 + }*/
126 + }
127 +
128 + private function validateAppleRootCa($header)
129 + {
130 + $lastIndex = count($header['x5c']) - 1;
131 +
132 + $certificate = $this->getCertificate($header['x5c'][$lastIndex]);
133 +
134 + if ($certificate != File::get(public_path(self::CA_PATH))) return false;
135 +
136 + return true;
137 + }
138 +
139 + private function getCertificate($string)
140 + {
141 + $certificate = "-----BEGIN CERTIFICATE-----" . PHP_EOL;
142 + $certificate .= chunk_split($string, 64, PHP_EOL);
143 + $certificate .= "-----END CERTIFICATE-----" . PHP_EOL;
144 + return $certificate;
145 + }
146 +
147 + private function decodeCertificate($string, $appleCertificate)
148 + {
149 + $certificate = $this->getCertificate($appleCertificate);
150 +
151 + $cert_object = openssl_x509_read($certificate);
152 + $pkey_object = openssl_pkey_get_public($cert_object);
153 + $pkey_array = openssl_pkey_get_details($pkey_object);
154 + $public_key = $pkey_array["key"];
155 +
156 + try{
157 + $decode = JWT::decode($string, new Key($public_key, "ES256"));
158 + return (array)$decode;
159 + }catch (SignatureInvalidException $exception){
160 + Log::error("Signature Invalid!");
161 + return false;
162 + }
62 } 163 }
63 } 164 }
...\ No newline at end of file ...\ No newline at end of file
......
1 +-----BEGIN CERTIFICATE-----
2 +MIICQzCCAcmgAwIBAgIILcX8iNLFS5UwCgYIKoZIzj0EAwMwZzEbMBkGA1UEAwwS
3 +QXBwbGUgUm9vdCBDQSAtIEczMSYwJAYDVQQLDB1BcHBsZSBDZXJ0aWZpY2F0aW9u
4 +IEF1dGhvcml0eTETMBEGA1UECgwKQXBwbGUgSW5jLjELMAkGA1UEBhMCVVMwHhcN
5 +MTQwNDMwMTgxOTA2WhcNMzkwNDMwMTgxOTA2WjBnMRswGQYDVQQDDBJBcHBsZSBS
6 +b290IENBIC0gRzMxJjAkBgNVBAsMHUFwcGxlIENlcnRpZmljYXRpb24gQXV0aG9y
7 +aXR5MRMwEQYDVQQKDApBcHBsZSBJbmMuMQswCQYDVQQGEwJVUzB2MBAGByqGSM49
8 +AgEGBSuBBAAiA2IABJjpLz1AcqTtkyJygRMc3RCV8cWjTnHcFBbZDuWmBSp3ZHtf
9 +TjjTuxxEtX/1H7YyYl3J6YRbTzBPEVoA/VhYDKX1DyxNB0cTddqXl5dvMVztK517
10 +IDvYuVTZXpmkOlEKMaNCMEAwHQYDVR0OBBYEFLuw3qFYM4iapIqZ3r6966/ayySr
11 +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2gA
12 +MGUCMQCD6cHEFl4aXTQY2e3v9GwOAEZLuN+yRhHFD/3meoyhpmvOwgPUnPWTxnS4
13 +at+qIxUCMG1mihDK1A3UT82NQz60imOlM27jbdoXt2QfyFMm+YhidDkLF1vLUagM
14 +6BgD56KyKA==
15 +-----END CERTIFICATE-----