Showing
2 changed files
with
120 additions
and
4 deletions
... | @@ -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 | ... | ... |
public/AppleRootCA-G3.pem
0 → 100644
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----- |
-
Please register or login to post a comment