@@ -77,36 +77,19 @@ private function checkPsr18Client(): void
7777 public static function getInvoicePayResultFromRequestArray (array $ requestParameters ): InvoicePayResult
7878 {
7979 return new InvoicePayResult (
80- outSum: $ requestParameters ['OutSum ' ] ?? throw new InvalidArgumentException (
81- 'OutSum request parameter required '
82- ),
80+ outSum: $ requestParameters ['OutSum ' ]
81+ ?? throw new InvalidArgumentException (
82+ 'OutSum request parameter required '
83+ ),
8384 invId: isset ($ requestParameters ['InvId ' ]) ? (int )$ requestParameters ['InvId ' ] : null ,
84- signatureValue: $ requestParameters ['SignatureValue ' ] ?? throw new InvalidArgumentException (
85- 'SignatureValue request parameter required '
86- ),
85+ signatureValue: $ requestParameters ['SignatureValue ' ]
86+ ?? throw new InvalidArgumentException (
87+ 'SignatureValue request parameter required '
88+ ),
8789 userParameters: self ::getUserParametersFromRequestArray ($ requestParameters ),
8890 );
8991 }
9092
91- /**
92- * Получение пользовательских параметров (shp_...) из массива GET или POST параметров
93- * @param array<string,string> $requestParameters
94- * @return array<string, string>
95- */
96- private static function getUserParametersFromRequestArray (array $ requestParameters ): array
97- {
98- $ userParameters = array_filter (
99- $ requestParameters ,
100- static fn ($ key ) => str_starts_with (strtolower ((string )$ key ), 'shp_ ' ),
101- ARRAY_FILTER_USE_KEY
102- );
103- $ userParametersWithoutPrefix = [];
104- foreach ($ userParameters as $ index => $ userParameter ) {
105- $ userParametersWithoutPrefix [substr_replace ($ index , '' , 0 , 4 )] = $ userParameter ;
106- }
107- return $ userParametersWithoutPrefix ;
108- }
109-
11093 public static function getVatsFromItems (array $ items ): array
11194 {
11295 $ vats = [];
@@ -121,38 +104,6 @@ public function checkSignature(InvoicePayResult $invoicePayResult): bool
121104 return $ this ->getValidSignatureForResult ($ invoicePayResult ) === strtolower ($ invoicePayResult ->signatureValue );
122105 }
123106
124- /**
125- * Получение правильной подписи для параметров результата {@see InvoicePayResult} от Робокассы
126- */
127- private function getValidSignatureForResult (InvoicePayResult $ invoicePayResult ): string
128- {
129- $ signature = "$ invoicePayResult ->outSum : $ invoicePayResult ->invId " ;
130- $ signature .= ": $ this ->password2 " ;
131- if (!empty ($ invoicePayResult ->userParameters )) {
132- $ signature .= ': ' . $ this ->implodeUserParameters ($ invoicePayResult ->userParameters );
133- }
134-
135- return strtolower ($ this ->encryptSignature ($ signature ));
136- }
137-
138- /**
139- * @param array<array-key, string> $additionalUserParameters
140- * @return string
141- */
142- private function implodeUserParameters (array $ additionalUserParameters ): string
143- {
144- ksort ($ additionalUserParameters );
145- foreach ($ additionalUserParameters as $ key => $ value ) {
146- $ additionalUserParameters [$ key ] = $ key . '= ' . $ value ;
147- }
148- return implode (': ' , $ additionalUserParameters );
149- }
150-
151- private function encryptSignature (string $ signature ): string
152- {
153- return hash ($ this ->hashAlgo , $ signature );
154- }
155-
156107 public function getPaymentUrl (InvoiceOptions $ invoiceOptions ): string
157108 {
158109 return $ this ->paymentUrl . '? ' . http_build_query ($ this ->getPaymentParameters ($ invoiceOptions ));
@@ -194,43 +145,6 @@ public function smsRequest(int $phone, string $message): RequestInterface
194145 return $ this ->getPsr18Client ()->createRequest ('GET ' , $ requestUri );
195146 }
196147
197- /**
198- * Формирование и кодирование подписи:
199- *
200- * Минимальный вариант подписи до кодирования
201- * MerchantLogin:OutSum:Пароль#1
202- *
203- * Максимальный вариант подписи до кодирования
204- * MerchantLogin:OutSum:InvId:OutSumCurrency:UserIp:Receipt:Пароль#1:Shp_...=...:Shp_...=...
205- */
206- private function generateSignatureForPayment (InvoiceOptions $ invoiceOptions ): string
207- {
208- $ signature = "$ this ->merchantLogin : $ invoiceOptions ->outSum : $ invoiceOptions ->invId " ;
209- $ signature .= isset ($ invoiceOptions ->outSumCurrency ) ? ": {$ invoiceOptions ->outSumCurrency ->value }" : '' ;
210- $ signature .= isset ($ invoiceOptions ->userIP ) ? ": $ invoiceOptions ->userIP " : '' ;
211- $ receipt = self ::getEncodedReceipt ($ invoiceOptions );
212- $ signature .= isset ($ receipt ) ? ": $ receipt " : '' ;
213- $ signature .= ": $ this ->password1 " ;
214- $ userParameters = $ invoiceOptions ->getFormattedUserParameters ();
215- $ signature .= !empty ($ userParameters ) ? ': ' . $ this ->implodeUserParameters ($ userParameters ) : '' ;
216-
217- return strtolower ($ this ->encryptSignature ($ signature ));
218- }
219-
220- /**
221- * @throws JsonException
222- */
223- private static function getEncodedReceipt (InvoiceOptions $ instance ): ?string
224- {
225- if (is_string ($ instance ->receipt )) {
226- return $ instance ->receipt ;
227- }
228- return is_null ($ instance ->receipt ) ? null : json_encode (
229- $ instance ->receipt ,
230- JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE
231- );
232- }
233-
234148 public function getReceiptAttachResult (ResponseInterface $ response ): ReceiptAttachResult
235149 {
236150 $ jsonObject = $ this ->parseResponseToJsonObject ($ response );
@@ -241,18 +155,6 @@ public function getReceiptAttachResult(ResponseInterface $response): ReceiptAtta
241155 );
242156 }
243157
244- private function parseResponseToJsonObject (ResponseInterface $ response ): object
245- {
246- /** @var object $jsonObject */
247- $ jsonObject = json_decode (
248- (string )$ response ->getBody (),
249- false ,
250- 512 ,
251- JSON_THROW_ON_ERROR
252- );
253- return $ jsonObject ;
254- }
255-
256158 public function getReceiptStatusResult (ResponseInterface $ response ): ReceiptStatusResult
257159 {
258160 $ jsonObject = $ this ->parseResponseToJsonObject ($ response );
@@ -320,21 +222,6 @@ public function getBase64SignedPostData(SecondReceiptOptions|ReceiptStatusOption
320222 return $ encodedSecondReceipt . '. ' . $ this ->generateSignatureForSecondReceipt ($ encodedSecondReceipt );
321223 }
322224
323- /**
324- * Робокасса требует удаление знаков "=" из результата кодирования base64
325- * @param string $string
326- * @return string
327- */
328- private function clearBase64Encode (string $ string ): string
329- {
330- return rtrim (base64_encode ($ string ), '= ' );
331- }
332-
333- private function generateSignatureForSecondReceipt (string $ encodedSecondReceipt ): string
334- {
335- return $ this ->clearBase64Encode ($ this ->encryptSignature ($ encodedSecondReceipt . $ this ->password1 ));
336- }
337-
338225 public function sendSecondReceiptAttach (SecondReceiptOptions $ secondReceiptOptions ): ResponseInterface
339226 {
340227 $ request = $ this ->secondReceiptAttachRequest ($ secondReceiptOptions );
@@ -375,4 +262,119 @@ public function setPsr18Client(?ClientInterface $psr18Client): void
375262 {
376263 $ this ->psr18Client = $ psr18Client ;
377264 }
265+
266+ /**
267+ * Получение пользовательских параметров (shp_...) из массива GET или POST параметров
268+ * @param array<string,string> $requestParameters
269+ * @return array<string, string>
270+ */
271+ private static function getUserParametersFromRequestArray (array $ requestParameters ): array
272+ {
273+ $ userParameters = array_filter (
274+ $ requestParameters ,
275+ static fn ($ key ) => str_starts_with (strtolower ((string )$ key ), 'shp_ ' ),
276+ ARRAY_FILTER_USE_KEY
277+ );
278+ $ userParametersWithoutPrefix = [];
279+ foreach ($ userParameters as $ index => $ userParameter ) {
280+ $ userParametersWithoutPrefix [substr_replace ($ index , '' , 0 , 4 )] = $ userParameter ;
281+ }
282+ return $ userParametersWithoutPrefix ;
283+ }
284+
285+ /**
286+ * Получение правильной подписи для параметров результата {@see InvoicePayResult} от Робокассы
287+ */
288+ private function getValidSignatureForResult (InvoicePayResult $ invoicePayResult ): string
289+ {
290+ $ signature = "$ invoicePayResult ->outSum : $ invoicePayResult ->invId " ;
291+ $ signature .= ": $ this ->password2 " ;
292+ if (!empty ($ invoicePayResult ->userParameters )) {
293+ $ signature .= ': ' . $ this ->implodeUserParameters ($ invoicePayResult ->userParameters );
294+ }
295+
296+ return strtolower ($ this ->encryptSignature ($ signature ));
297+ }
298+
299+ /**
300+ * @param array<array-key, string> $additionalUserParameters
301+ * @return string
302+ */
303+ private function implodeUserParameters (array $ additionalUserParameters ): string
304+ {
305+ ksort ($ additionalUserParameters );
306+ foreach ($ additionalUserParameters as $ key => $ value ) {
307+ $ additionalUserParameters [$ key ] = $ key . '= ' . $ value ;
308+ }
309+ return implode (': ' , $ additionalUserParameters );
310+ }
311+
312+ private function encryptSignature (string $ signature ): string
313+ {
314+ return hash ($ this ->hashAlgo , $ signature );
315+ }
316+
317+ private function parseResponseToJsonObject (ResponseInterface $ response ): object
318+ {
319+ /** @var object $jsonObject */
320+ $ jsonObject = json_decode (
321+ (string )$ response ->getBody (),
322+ false ,
323+ 512 ,
324+ JSON_THROW_ON_ERROR
325+ );
326+ return $ jsonObject ;
327+ }
328+
329+ /**
330+ * @throws JsonException
331+ */
332+ private static function getEncodedReceipt (InvoiceOptions $ instance ): ?string
333+ {
334+ if (is_string ($ instance ->receipt )) {
335+ return $ instance ->receipt ;
336+ }
337+ return is_null ($ instance ->receipt ) ? null : json_encode (
338+ $ instance ->receipt ,
339+ JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE
340+ );
341+ }
342+
343+ /**
344+ * Робокасса требует удаление знаков "=" из результата кодирования base64
345+ * @param string $string
346+ * @return string
347+ */
348+ private function clearBase64Encode (string $ string ): string
349+ {
350+ return rtrim (base64_encode ($ string ), '= ' );
351+ }
352+
353+ /**
354+ * Формирование и кодирование подписи:
355+ *
356+ * Минимальный вариант подписи до кодирования
357+ * MerchantLogin:OutSum:Пароль#1
358+ *
359+ * Максимальный вариант подписи до кодирования
360+ * MerchantLogin:OutSum:InvId:OutSumCurrency:UserIp:Receipt:Пароль#1:Shp_...=...:Shp_...=...
361+ */
362+ private function generateSignatureForPayment (InvoiceOptions $ invoiceOptions ): string
363+ {
364+ $ signature = "$ this ->merchantLogin : $ invoiceOptions ->outSum : $ invoiceOptions ->invId " ;
365+ $ signature .= isset ($ invoiceOptions ->outSumCurrency ) ? ": {$ invoiceOptions ->outSumCurrency ->value }" : '' ;
366+ $ signature .= isset ($ invoiceOptions ->userIP ) ? ": $ invoiceOptions ->userIP " : '' ;
367+ $ receipt = self ::getEncodedReceipt ($ invoiceOptions );
368+ $ signature .= isset ($ receipt ) ? ": $ receipt " : '' ;
369+ $ signature .= ": $ this ->password1 " ;
370+ $ userParameters = $ invoiceOptions ->getFormattedUserParameters ();
371+ $ signature .= !empty ($ userParameters ) ? ': ' . $ this ->implodeUserParameters ($ userParameters ) : '' ;
372+
373+ return strtolower ($ this ->encryptSignature ($ signature ));
374+ }
375+
376+ private function generateSignatureForSecondReceipt (string $ encodedSecondReceipt ): string
377+ {
378+ return $ this ->clearBase64Encode ($ this ->encryptSignature ($ encodedSecondReceipt . $ this ->password1 ));
379+ }
378380}
0 commit comments