diff --git a/src/Encryptor.php b/src/Encryptor.php index 02d8de1..73c36a6 100644 --- a/src/Encryptor.php +++ b/src/Encryptor.php @@ -33,47 +33,37 @@ public function decrypt(string $plainText): string public function tradeSha($data) { - if (is_array($data)) { - ksort($data); - $plainText = http_build_query($data); - } else { - $plainText = $data; - } - - return strtoupper(hash( - "sha256", - implode('&', ['HashKey='.$this->hashKey, $plainText, 'HashIV='.$this->hashIv]) - )); + return $this->makeHash([ + 'HashKey='.$this->hashKey, + $this->toPlainText($data), + 'HashIV='.$this->hashIv, + ]); } public function checkValue($data) { if (is_array($data)) { - ksort($data); - $plainText = http_build_query($data); - } else { - $plainText = $data; + $data = self::only($data, ['MerchantID', 'Amt', 'MerchantOrderNo']); } - return strtoupper(hash( - "sha256", - implode('&', ['IV='.$this->hashIv, $plainText, 'Key='.$this->hashKey]) - )); + return $this->makeHash([ + 'IV='.$this->hashIv, + $this->toPlainText($data), + 'Key='.$this->hashKey, + ]); } public function checkCode($data) { if (is_array($data)) { - ksort($data); - $plainText = http_build_query($data); - } else { - $plainText = $data; + $data = self::only($data, ['MerchantID', 'Amt', 'MerchantOrderNo', 'TradeNo']); } - return strtoupper(hash( - "sha256", - implode('&', ['HashIV='.$this->hashIv, $plainText, 'HashKey='.$this->hashKey]) - )); + return $this->makeHash([ + 'HashIV='.$this->hashIv, + $this->toPlainText($data), + 'HashKey='.$this->hashKey, + ]); } private function stripPadding($value) @@ -89,4 +79,37 @@ private function paddingIsValid($pad, $value) return substr($value, $beforePad) === str_repeat(substr($value, -1), $pad); } + + /** + * @param string|array $data + * @return string + */ + private function toPlainText($data) + { + if (! is_array($data)) { + $plainText = $data; + } else { + ksort($data); + $plainText = http_build_query($data); + } + + return $plainText; + } + + private function makeHash(array $data) + { + return strtoupper(hash("sha256", implode('&', $data))); + } + + private static function only(array $array, array $keys = []) + { + $result = []; + foreach ($keys as $key) { + if (array_key_exists($key, $array)) { + $result[$key] = $array[$key]; + } + } + + return $result; + } } diff --git a/src/Message/AcceptNotificationRequest.php b/src/Message/AcceptNotificationRequest.php index 04288d7..6df44da 100644 --- a/src/Message/AcceptNotificationRequest.php +++ b/src/Message/AcceptNotificationRequest.php @@ -2,19 +2,13 @@ namespace Omnipay\NewebPay\Message; -use Omnipay\Common\Exception\InvalidResponseException; use Omnipay\Common\Message\NotificationInterface; class AcceptNotificationRequest extends CompletePurchaseRequest implements NotificationInterface { - /** - * @param array $data - * @return AcceptNotificationResponse - * @throws InvalidResponseException - */ public function sendData($data) { - return $this->response = new AcceptNotificationResponse($this, $this->decrypt($data)); + return $this->response = new AcceptNotificationResponse($this, $data); } public function getTransactionId() @@ -38,7 +32,7 @@ public function getMessage() } /** - * @return CompletePurchaseResponse + * @return AcceptNotificationResponse */ private function getNotificationResponse() { diff --git a/src/Message/CompletePurchaseRequest.php b/src/Message/CompletePurchaseRequest.php index 39312ab..fe36780 100644 --- a/src/Message/CompletePurchaseRequest.php +++ b/src/Message/CompletePurchaseRequest.php @@ -3,41 +3,29 @@ namespace Omnipay\NewebPay\Message; use Omnipay\Common\Exception\InvalidResponseException; -use Omnipay\NewebPay\Encryptor; use Omnipay\NewebPay\Traits\HasDefaults; class CompletePurchaseRequest extends AbstractRequest { use HasDefaults; - public function getData() - { - return $this->httpRequest->request->all(); - } - - /** - * @throws InvalidResponseException - */ - public function sendData($data) - { - return $this->response = new CompletePurchaseResponse($this, $this->decrypt($data)); - } - /** * @throws InvalidResponseException */ - protected function decrypt($data) + public function getData() { - $encryptor = new Encryptor($this->getHashKey(), $this->getHashIv()); - $tradeSha = $encryptor->tradeSha($data['TradeInfo']); - + $data = $this->httpRequest->request->all(); + $tradeSha = $this->tradeSha($data['TradeInfo']); if (! hash_equals($tradeSha, $data['TradeSha'])) { - throw new InvalidResponseException(); + throw new InvalidResponseException('Incorrect TradeSha'); } - - $data['Result'] = []; - parse_str($encryptor->decrypt($data['TradeInfo']), $data['Result']); + $data['Result'] = $this->decrypt($data['TradeInfo']); return $data; } + + public function sendData($data) + { + return $this->response = new CompletePurchaseResponse($this, $data); + } } diff --git a/src/Message/FetchTransactionRequest.php b/src/Message/FetchTransactionRequest.php index 0b294a6..6fba2d0 100644 --- a/src/Message/FetchTransactionRequest.php +++ b/src/Message/FetchTransactionRequest.php @@ -4,7 +4,6 @@ use Omnipay\Common\Exception\InvalidRequestException; use Omnipay\Common\Exception\InvalidResponseException; -use Omnipay\NewebPay\Encryptor; use Omnipay\NewebPay\Traits\HasDefaults; class FetchTransactionRequest extends AbstractRequest @@ -48,17 +47,10 @@ public function getGateway() */ public function getData() { - $encryptor = new Encryptor($this->getHashKey(), $this->getHashIv()); - - return array_filter([ + $data = array_filter([ 'MerchantID' => $this->getMerchantID(), 'Version' => $this->getVersion() ?: '1.3', 'RespondType' => $this->getRespondType(), - 'CheckValue' => $encryptor->checkValue([ - 'Amt' => (int) $this->getAmount(), - 'MerchantID' => $this->getMerchantID(), - 'MerchantOrderNo' => $this->getTransactionId(), - ]), 'TimeStamp' => $this->getTimeStamp(), 'MerchantOrderNo' => $this->getTransactionId(), 'Amt' => (int) $this->getAmount(), @@ -66,6 +58,10 @@ public function getData() ], static function ($value) { return $value !== null && $value !== ''; }); + + $data['CheckValue'] = $this->checkValue($data); + + return $data; } /** @@ -78,14 +74,7 @@ public function sendData($data) ], http_build_query($data)); $result = json_decode((string) $response->getBody(), true); - $encryptor = new Encryptor($this->getHashKey(), $this->getHashIv()); - - if (! hash_equals($result['Result']['CheckCode'], $encryptor->checkCode([ - 'MerchantID' => $result['Result']['MerchantID'], - 'Amt' => $result['Result']['Amt'], - 'MerchantOrderNo' => $result['Result']['MerchantOrderNo'], - 'TradeNo' => $result['Result']['TradeNo'], - ]))) { + if (! hash_equals($result['Result']['CheckCode'], $this->checkCode($result['Result']))) { throw new InvalidResponseException('Incorrect CheckCode'); } diff --git a/src/Message/PurchaseResponse.php b/src/Message/PurchaseResponse.php index bef8c51..f7a9737 100644 --- a/src/Message/PurchaseResponse.php +++ b/src/Message/PurchaseResponse.php @@ -3,7 +3,6 @@ namespace Omnipay\NewebPay\Message; use Omnipay\Common\Message\RedirectResponseInterface; -use Omnipay\NewebPay\Encryptor; class PurchaseResponse extends AbstractResponse implements RedirectResponseInterface { @@ -29,13 +28,12 @@ public function getRedirectMethod() public function getRedirectData() { - $encryptor = new Encryptor($this->request->getHashKey(), $this->request->getHashIv()); - $tradeInfo = $encryptor->encrypt($this->data); + $tradeInfo = $this->request->encrypt($this->data); return [ 'MerchantID' => $this->data['MerchantID'], 'TradeInfo' => $tradeInfo, - 'TradeSha' => $encryptor->tradeSha($tradeInfo), + 'TradeSha' => $this->request->tradeSha($tradeInfo), 'Version' => $this->data['Version'], ]; } diff --git a/src/Traits/HasDefaults.php b/src/Traits/HasDefaults.php index 2ee5cec..485665c 100644 --- a/src/Traits/HasDefaults.php +++ b/src/Traits/HasDefaults.php @@ -3,6 +3,7 @@ namespace Omnipay\NewebPay\Traits; use Omnipay\Common\Exception\InvalidRequestException; +use Omnipay\NewebPay\Encryptor; trait HasDefaults { @@ -156,4 +157,42 @@ public function getAmt() { return (int) $this->getAmount(); } + + public function encrypt(array $data) + { + $encryptor = new Encryptor($this->getHashKey(), $this->getHashIv()); + + return $encryptor->encrypt($data); + } + + public function decrypt(string $plainText) + { + $encryptor = new Encryptor($this->getHashKey(), $this->getHashIv()); + + $data = []; + parse_str($encryptor->decrypt($plainText), $data); + + return $data; + } + + public function tradeSha($plainText) + { + $encryptor = new Encryptor($this->getHashKey(), $this->getHashIv()); + + return $encryptor->tradeSha($plainText); + } + + public function checkValue($data) + { + $encryptor = new Encryptor($this->getHashKey(), $this->getHashIv()); + + return $encryptor->checkValue($data); + } + + public function checkCode($data) + { + $encryptor = new Encryptor($this->getHashKey(), $this->getHashIv()); + + return $encryptor->checkCode($data); + } } diff --git a/tests/Message/CompletePurchaseRequestTest.php b/tests/Message/CompletePurchaseRequestTest.php index 72f5a7c..597bc08 100644 --- a/tests/Message/CompletePurchaseRequestTest.php +++ b/tests/Message/CompletePurchaseRequestTest.php @@ -34,6 +34,31 @@ public function testGetData(): array 'Version' => '2.0', 'TradeInfo' => 'ee11d1501e6dc8433c75988258f2343d11f4d0a423be672e8e02aaf373c53c2363aeffdb4992579693277359b3e449ebe644d2075fdfbc10150b1c40e7d24cb215febefdb85b16a5cde449f6b06c58a5510d31e8d34c95284d459ae4b52afc1509c2800976a5c0b99ef24cfd28a2dfc8004215a0c98a1d3c77707773c2f2132f9a9a4ce3475cb888c2ad372485971876f8e2fec0589927544c3463d30c785c2d3bd947c06c8c33cf43e131f57939e1f7e3b3d8c3f08a84f34ef1a67a08efe177f1e663ecc6bedc7f82640a1ced807b548633cfa72d060864271ec79854ee2f5a170aa902000e7c61d1269165de330fce7d10663d1668c711571776365bfdcd7ddc915dcb90d31a9f27af9b79a443ca8302e508b0dbaac817d44cfc44247ae613075dde4ac960f1bdff4173b915e4344bc4567bd32e86be7d796e6d9b9cf20476e4996e98ccc315f1ed03a34139f936797d971f2a3f90bc18f8a155a290bcbcf04f4277171c305bf554f5cba243154b30082748a81f2e5aa432ef9950cc9668cd4330ef7c37537a6dcb5e6ef01b4eca9705e4b097cf6913ee96e81d0389e5f775', 'TradeSha' => 'C80876AEBAC0036268C0E240E5BFF69C0470DE9606EEE083C5C8DD64FDB3347A', + 'Result' => [ + 'Status' => 'SUCCESS', + 'Message' => '授權成功', + 'MerchantID' => 'MS127874575', + 'Amt' => '30', + 'TradeNo' => '23092714215835071', + 'MerchantOrderNo' => 'Vanespl_ec_1695795668', + 'RespondType' => 'String', + 'IP' => '123.51.237.115', + 'EscrowBank' => 'HNCB', + 'PaymentType' => 'CREDIT', + 'RespondCode' => '00', + 'Auth' => '115468', + 'Card6No' => '400022', + 'Card4No' => '1111', + 'Exp' => '2609', + 'AuthBank' => 'KGI', + 'TokenUseStatus' => '0', + 'InstFirst' => '0', + 'InstEach' => '0', + 'Inst' => '0', + 'ECI' => '', + 'PayTime' => '2023-09-27 14:21:59', + 'PaymentMethod' => 'CREDIT', + ], ], $data); return [$request->send(), $data];