2023-11-13 21:20:10 +08:00
<? php
namespace wchat\wx\V3 ;
use Exception ;
2023-11-13 22:57:36 +08:00
use OpenSSLAsymmetricKey ;
2023-11-13 22:13:44 +08:00
use Psr\Http\Message\RequestInterface ;
2023-11-13 21:20:10 +08:00
use wchat\wx\SmallProgram ;
use wchat\wx\V3\Notify\GoodsDetail ;
use wchat\wx\V3\Notify\NotifyModel ;
use wchat\wx\V3\Notify\PromotionDetail ;
2023-11-13 22:37:49 +08:00
const KEY_TYPE_PUBLIC = 'public' ;
const KEY_TYPE_PRIVATE = 'private' ;
2023-11-13 22:13:44 +08:00
2023-11-13 21:20:10 +08:00
class WxV3PaymentNotify extends SmallProgram
{
use WxV3PaymentTait ;
/**
* @param string $id
* @param string $create_time
* @param string $resource_type
* @param string $event_type
* @param string $summary
* @param array $resource
*/
public function __construct (
2023-11-14 14:10:36 +08:00
public string $id = "EV-2018022511223320873" ,
public string $create_time = "2015-05-20T13:29:35+08:00" ,
public string $resource_type = "encrypt-resource" ,
public string $event_type = "TRANSACTION.SUCCESS" ,
public string $summary = "支付成功" ,
public array $resource = []
2023-11-13 21:20:10 +08:00
)
{
}
/**
* @var NotifyModel
*/
public NotifyModel $notifyModel ;
2023-11-13 22:13:44 +08:00
/**
* @param RequestInterface $request
* @return bool
2023-12-12 15:35:37 +08:00
* @throws
2023-11-13 22:13:44 +08:00
*/
public function verify ( RequestInterface $request ) : bool
{
2024-06-03 11:07:46 +08:00
$platformPublicKeyInstance = $this -> rsaFrom ( $this -> payConfig -> pay -> wx -> mchCert , KEY_TYPE_PUBLIC );
2023-11-13 22:42:26 +08:00
$inWechatpaySignature = $request -> getHeaderLine ( 'Wechatpay-Signature' ); // 请根据实际情况获取
$inWechatpayTimestamp = $request -> getHeaderLine ( 'Wechatpay-Timestamp' ); // 请根据实际情况获取
$inWechatpayNonce = $request -> getHeaderLine ( 'Wechatpay-Nonce' ); // 请根据实际情况获取
$inBody = $request -> getBody () -> getContents (); // 请根据实际情况获取,例如: file_get_contents('php://input');
2023-11-13 22:14:14 +08:00
$timeOffsetStatus = 300 >= abs ( time () - ( int ) $inWechatpayTimestamp );
$verifiedStatus = $this -> notifyVerify (
$this -> lineFeed ([ $inWechatpayTimestamp , $inWechatpayNonce , $inBody ]),
$inWechatpaySignature ,
$platformPublicKeyInstance );
2023-11-13 22:13:44 +08:00
if ( ! $timeOffsetStatus || ! $verifiedStatus ) {
return false ;
}
2023-11-13 22:37:49 +08:00
return $this -> decode ( $this -> resource [ 'ciphertext' ], $this -> resource [ 'nonce' ], $this -> resource [ 'associated_data' ]);
2023-11-13 22:13:44 +08:00
}
/**
* @param ...$pieces
* @return string
*/
protected function lineFeed ( ... $pieces ) : string
{
return implode ( " \n " , array_merge ( $pieces , [ '' ]));
}
/**
* @return string|bool
*/
protected function body () : string | bool
{
return json_encode ([ 'id' => $this -> id , 'create_time' => $this -> create_time , 'resource_type' => $this -> resource_type , 'event_type' => $this -> event_type , 'summary' => $this -> summary , 'resource' => $this -> resource ]);
}
/**
* @param string $message
* @param string $signature
2023-11-14 14:10:36 +08:00
* @param OpenSSLAsymmetricKey $publicKey
2023-11-13 22:13:44 +08:00
* @return bool
*/
2023-11-14 14:10:36 +08:00
protected function notifyVerify ( string $message , string $signature , OpenSSLAsymmetricKey $publicKey ) : bool
2023-11-13 22:13:44 +08:00
{
if (( $result = openssl_verify ( $message , base64_decode ( $signature ), $publicKey , OPENSSL_ALGO_SHA256 )) === false ) {
throw new \UnexpectedValueException ( 'Verified the input $message failed, please checking your $publicKey whether or nor correct.' );
}
return $result === 1 ;
}
/**
2023-11-14 14:10:36 +08:00
* @param string $thing
2023-11-13 22:13:44 +08:00
* @param string $type
2023-11-13 22:57:36 +08:00
* @return OpenSSLAsymmetricKey
2023-11-13 22:13:44 +08:00
*/
2023-11-14 14:10:36 +08:00
protected function rsaFrom ( string $thing , string $type = KEY_TYPE_PRIVATE ) : OpenSSLAsymmetricKey
2023-11-13 22:13:44 +08:00
{
2024-06-03 11:07:46 +08:00
$pkey = (( $isPublic = $type === KEY_TYPE_PUBLIC ) ? openssl_pkey_get_public ( 'file://' . $thing ) : openssl_pkey_get_private ( 'file://' . $thing ));
2023-11-13 22:13:44 +08:00
if ( false === $pkey ) {
2023-11-13 22:57:36 +08:00
throw new \UnexpectedValueException ( sprintf ( 'Cannot load %s from(%s), please take care about the $thing input.' , $isPublic ? 'publicKey' : 'privateKey' , gettype ( $thing )));
2023-11-13 22:13:44 +08:00
}
return $pkey ;
}
2023-11-13 21:20:10 +08:00
/**
2023-11-14 14:10:36 +08:00
* @param string $ciphertext
* @param string $nonce
* @param string $associated_data
2023-11-13 22:37:49 +08:00
* @return bool
2023-11-13 21:20:10 +08:00
*/
2023-11-14 14:10:36 +08:00
public function decode ( string $ciphertext , string $nonce , string $associated_data ) : bool
2023-11-13 21:20:10 +08:00
{
2023-11-14 00:06:57 +08:00
$data = $this -> decrypt ( $ciphertext , $this -> payConfig -> pay -> wx -> secret , $nonce , $associated_data );
2023-11-13 21:20:10 +08:00
$this -> notifyModel = new NotifyModel ();
$this -> notifyModel -> amount = $data [ 'amount' ];
$this -> notifyModel -> payer = $data [ 'payer' ];
$this -> notifyModel -> scene_info = $data [ 'payer' ];
$this -> notifyModel -> appid = $data [ 'appid' ];
$this -> notifyModel -> mchid = $data [ 'mchid' ];
$this -> notifyModel -> out_trade_no = $data [ 'out_trade_no' ];
$this -> notifyModel -> transaction_id = $data [ 'transaction_id' ];
$this -> notifyModel -> trade_type = $data [ 'trade_type' ];
$this -> notifyModel -> trade_state = $data [ 'trade_state' ];
$this -> notifyModel -> trade_state_desc = $data [ 'trade_state_desc' ];
$this -> notifyModel -> bank_type = $data [ 'bank_type' ];
$this -> notifyModel -> attach = $data [ 'attach' ];
$this -> notifyModel -> success_time = $data [ 'success_time' ];
$this -> notifyModel -> promotion_detail = [];
foreach ( $data [ 'promotion_detail' ] as $datum ) {
$detail = new PromotionDetail ();
$detail -> amount = $datum [ 'amount' ];
$detail -> wechatpay_contribute = $datum [ 'wechatpay_contribute' ];
$detail -> coupon_id = $datum [ 'coupon_id' ];
$detail -> scope = $datum [ 'scope' ];
$detail -> merchant_contribute = $datum [ 'merchant_contribute' ];
$detail -> name = $datum [ 'name' ];
$detail -> other_contribute = $datum [ 'other_contribute' ];
$detail -> currency = $datum [ 'currency' ];
$detail -> stock_id = $datum [ 'stock_id' ];
$detail -> goods_detail = [];
foreach ( $datum [ 'goods_detail' ] as $value ) {
$detail -> goods_detail [] = new GoodsDetail ( $value );
}
$this -> notifyModel -> promotion_detail [] = $detail ;
}
2023-11-13 22:37:49 +08:00
return true ;
2023-11-13 21:20:10 +08:00
}
}