- 关于微信小程序的一次性订阅消息怎么实现在 微信-发现-小程序栏上展示相关小程序的消息 ?
[图片]如图 瑞幸是怎么实现的呢
01-18 - 微信API加密,排坑整理,PHP版本
微信推出了API加密解密的写法,主要应用于一些涉及交易的接口,如同城配送~,在对接同城配送的过程中,我踩了许多的坑,自身原因一部分、文档原因一部分,现在就来讲讲PHP如何一步步的实现解决API加密、解密的写法: 1.首先在 开发->开发管理->AP安全 ,设置几个密钥,并获取平台证书[图片] [图片] 2.直接上代码。 <?php /** * 封装微信api签名安全类 */ namespace common\components\wxapi; use common\helpers\MiniHelper; use phpseclib\Crypt\RSA; use Yii; use yii\base\ErrorException; class WxApi { private $appId; private $aes; private $rsa; private $cert; private $url; public function __construct() { $this->appId = Yii::$app->params['miniWechat']['appId']; $wechatSafeApi = Yii::$app->params['wechatSafeApi']; $this->aes['sn'] = $wechatSafeApi['aes-sn']; $this->aes['key'] = file_get_contents(dirname(Yii::$app->getBasePath()) . $wechatSafeApi['aes-key']); $this->rsa['sn'] = $wechatSafeApi['rsa-sn']; $this->rsa['rsa-public-key'] = file_get_contents(dirname(Yii::$app->getBasePath()) . $wechatSafeApi['rsa-public-key']); $this->rsa['rsa-private-key'] = file_get_contents(dirname(Yii::$app->getBasePath()) . $wechatSafeApi['rsa-private-key']); $this->cert['sn'] = $wechatSafeApi['cert-sn']; $this->cert['cert-key'] = file_get_contents(dirname(Yii::$app->getBasePath()) . $wechatSafeApi['cert-key']); } /** * Name:对外方法用于所有微信api的请求方法 * User: zcw * Date: 2023/7/14 * Time: 9:51 * @param $url * @param $req * @throws ErrorException * @throws \Exception */ public function request($url, $req) { $accessToken = $this->getAccessToken(); $this->url = $url; $urls = $url . "?access_token=" . $accessToken; //1.数据加密 $newRe = $this->getRequestParam($url, $req); //2.获取签名 $signature = $this->getSignature($newRe); //本地验签 非必需 $checkLocalSig = $this->checkLocalSignature($newRe, $signature); if (!$checkLocalSig) { throw new ErrorException('本地验签错误'); } $appId = $this->appId; $headerArray = ['Wechatmp-Appid:' . $appId, 'Wechatmp-TimeStamp:' . $newRe['ts'], 'Wechatmp-Signature:' . $signature]; $data = $this->curlPost($urls, $newRe['reqData'], $headerArray); $headers = $this->httpParseHeaders($data['header']); $body = \Qiniu\json_decode($data['body'], true); //请求平台报错 if (isset($body['errcode'])) { throw new ErrorException($body['errmsg']); } // 3.响应参数验签 目前存在问题 $vertify = $this->vertifyResponse($data); //4.参数解密 return $this->jM($headers['Wechatmp-TimeStamp'], $body); } /** * Name:获取accessToken * User: zcw * Date: 2023/7/14 * Time: 9:15 */ public function getAccessToken() { $qr = new MiniHelper(); return $qr->getAccessToken(); } /** * Name:post请求 * User: zcw * Date: 2023/7/14 * Time: 9:19 * @param $url * @param $field * @param $header * @return array */ public function curlPost($url, $field, $header) { $headerArray = array("Content-type:application/json;charset=utf-8", "Accept:application/json"); $headerArray = array_merge($headerArray, $header); $curl = curl_init(); curl_setopt($curl, CURLOPT_HTTPHEADER, $headerArray); curl_setopt($curl, CURLOPT_URL, $url); curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); curl_setopt($curl, CURLOPT_POST, 1); curl_setopt($curl, CURLOPT_POSTFIELDS, $field); //输出响应头部 curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_HEADER, true); $str = curl_exec($curl); $headerSize = curl_getinfo($curl, CURLINFO_HEADER_SIZE); $headers = substr($str, 0, $headerSize); $body = substr($str, $headerSize); curl_close($curl); return ['body' => $body, 'header' => $headers]; } /** * Name:对外方法用于所有微信api的请求通道 * User: zcw * Date: 2023/7/14 * Time: 9:21 * @param $url * @param $req * @return array * @throws \Exception */ public function getRequestParam($url, $req) { $key = base64_decode($this->aes['key']); $sn = $this->aes['sn']; $appId = $this->appId; $time = time(); //16位随机字符 $nonce = rtrim(base64_encode(random_bytes(16)), '='); $addReq = ["_n" => $nonce, "_appid" => $appId, "_timestamp" => $time]; $realReq = array_merge($addReq, $req); $realReq = json_encode($realReq); //额外参数 $aad = $url . "|" . $appId . "|" . $time . "|" . $sn; //12位随机字符 $iv = random_bytes(12); $cipher = openssl_encrypt($realReq, "aes-256-gcm", $key, OPENSSL_RAW_DATA, $iv, $tag, $aad); $iv = base64_encode($iv); $data = base64_encode($cipher); $authTag = base64_encode($tag); $reqData = ["iv" => $iv, "data" => $data, "authtag" => $authTag]; //校验本地加密是否正确 非必须 // $checkParam = $this->checkParam($key, $authTag, $iv, $data, $aad); return ['ts' => $time, 'reqData' => json_encode($reqData)]; } /** * Name:请求前本地验签 * User: zcw * Date: 2023/7/14 * Time: 9:57 * @param $key * @param $authTag * @param $iv * @param $data * @param $aad */ private function checkParam($key, $authTag, $iv, $data, $aad) { $iv = base64_decode($iv); $data = base64_decode($data); $authTag = base64_decode($authTag); return openssl_decrypt($data, "aes-256-gcm", $key, OPENSSL_RAW_DATA, $iv, $authTag, $aad); } /** * Name:获取签名 * User: zcw * Date: 2023/7/14 * Time: 10:03 * @param array $newRe */ private function getSignature(array $newRe) { $time = $newRe['ts']; $key = $this->rsa['rsa-private-key']; $url = $this->url; $appId = $this->appId; $reqData = $newRe['reqData']; $payload = "$url\n$appId\n$time\n$reqData"; $rsa = new RSA(); $rsa->loadKey($key); $rsa->setHash("sha256"); $rsa->setMGFHash("sha256"); $signature = $rsa->sign($payload); return base64_encode($signature); } /** * Name:请求前本地验签 * User: zcw * Date: 2023/7/14 * Time: 10:11 * @param array $newRe * @param string $signature */ private function checkLocalSignature(array $newRe, string $signature) { $signature = base64_decode($signature); $rsaPubKey = $this->rsa['rsa-public-key']; $appId = $this->appId; $url = $this->url; $time = $newRe['ts']; $reqData = $newRe['reqData']; $payload = "$url\n$appId\n$time\n$reqData"; $payload = utf8_encode($payload); $rsa = new RSA(); $rsa->loadKey($rsaPubKey); $rsa->setHash("sha256"); $rsa->setMGFHash("sha256"); return $rsa->verify($payload, $signature); } /** * Name:解析头部信息 * User: zcw * Date: 2023/7/14 * Time: 10:28 * @param $headerString * @return array */ private function httpParseHeaders($headerString) { $headers = []; $lines = explode("\r\n", $headerString); foreach ($lines as $line) { $line = trim($line); if (!empty($line)) { $parts = explode(':', $line, 2); $key = trim($parts[0]); $value = isset($parts[1]) ? trim($parts[1]) : ''; $headers[$key] = $value; } } return $headers; } /** * Name:解密参数 * User: zcw * Date: 2023/7/14 * Time: 10:31 * @param $ts * @param $body * @return mixed|null * @throws ErrorException */ private function jM($ts, $body) { $url = $this->url; $appId = $this->appId; $sn = $this->aes['sn']; $aad = $url . '|' . $appId . '|' . $ts . '|' . $sn; $key = $this->aes['key']; $key = base64_decode($key); $iv = base64_decode($body['iv']); $data = base64_decode($body['data']); $authTag = base64_decode($body['authtag']); $result = openssl_decrypt($data, "aes-256-gcm", $key, OPENSSL_RAW_DATA, $iv, $authTag, $aad); if (!$result) { throw new ErrorException(); } $result = \Qiniu\json_decode($result,true); return $result; } /** * Name:验证响应值 * User: zcw * Date: 2023/7/14 * Time: 11:16 * @param $data */ private function vertifyResponse($data) { $headers = $this->httpParseHeaders($data['header']); $nowTime = time(); $reTime = $headers['Wechatmp-TimeStamp']; $appId = $this->appId; $cert = $this->cert; $sn = $cert['sn']; $key = $cert['cert-key']; $url = $this->url; if ($appId != $headers['Wechatmp-Appid'] || $nowTime - $reTime > 300){ throw new \ErrorException('返回值安全字段校验失败'); } if ($sn == $headers['Wechatmp-Serial']) { $signature = $headers['Wechatmp-Signature']; } elseif ($sn == $headers['Wechatmp-Serial-Deprecated']) { $signature = $headers['Wechatmp-Signature-Deprecated']; } else { throw new \ErrorException('返回值sn不匹配'); } $reData = $data['body']; $payload = "$url\n$appId\n$reTime\n$reData"; $payload = utf8_encode($payload); $signature = base64_decode($signature); $rsa = new RSA(); $rsa->loadKey($key); $rsa->setHash("sha256"); $rsa->setMGFHash("sha256"); return $rsa->verify($payload, $signature); } } 以上是我的代码 ,每个方法都有明确注释,除了返回值使用平台证书未通过以外 ,其余几个环节均正常,大家也可以帮我找找平台证书对返回数据验签失败的原因,有需要也可以加我微信 abc1783475843
2023-07-24 - php使用同城配送时接入微信api安全出错,报错:934011,怎么解决?
报错信息:string(86) "{"errcode":934011,"errmsg":"request_body is required rid: 64adfa6f-4b7c30ee-0a560363"}" 我的代码:其中app_id local_sn local_key等均已省略 <?php namespace backend\modules\v4\controllers; use backend\components\BaseApiController; use backend\components\helper\WxDelivery; class TestController extends BaseApiController { private $param = [ 'app_id' => '', 'url' => 'https://api.weixin.qq.com/cgi-bin/express/intracity/balancequery', 'local_sn' => '', 'local_key' => '', 'store_id' => '', 'out_store_id' => '', ]; private $key =[ 'sn'=>'', 'key'=>'-----BEGIN RSA PRIVATE KEY----- 省略 -----END RSA PRIVATE KEY-----' ]; public function actionTest() { $param = $this->param; $accessToken = WxDelivery::getAccessToken(); $url = $this->param['url'] . "?access_token=" . $accessToken; $newRe = $this->getRequestParam(); $signature = $this->getSignature($newRe); $headerArray =['Wechatmp-Appid'=>$param['app_id'],'Wechatmp-TimeStamp'=>$newRe['ts'],'Wechatmp-Signature'=>$signature]; $data = WxDelivery::curlPost($url, $newRe['reqData'],$headerArray); var_dump($data); } public function getRequestParam() { $param = $this->param; // $param['local_key'] = base64_decode($param['local_key']); $req = ['store_id' => $param['store_id'], 'out_store_id' => $param['out_store_id']]; $time = time(); //16位随机字符串 $nonce = rtrim(base64_encode(random_bytes(16)), '='); $addReq = ["_n" => $nonce, "_appid" => $param['app_id'], "_timestamp" => $time]; $realReq = array_merge($addReq, $req); $realReq = json_encode($realReq); //额外参数 $aad = $param['url'] . "|" . $param['app_id'] . "|" . $time . "|" . $param['local_sn']; //12位随机字符 $iv = random_bytes(12); $cipher = openssl_encrypt($realReq, "aes-256-gcm", $param['local_key'], OPENSSL_RAW_DATA, $iv, $tag, $aad); $iv = base64_encode($iv); $data = base64_encode($cipher); $authTag = base64_encode($tag); $reqData = ["iv" => $iv, "data" => $data, "authtag" => $authTag]; return ['ts' => $time, 'reqData' => json_encode($reqData)]; } public function getSignature($newRe) { $param = $this->param; $keys = $this->key; $time = $newRe['ts']; $url = $param['url']; $appId = $param['app_id']; $reqData = $newRe['reqData']; $payload = "$url\n$appId\n$time\n$reqData"; $key = openssl_pkey_get_private($keys['key']); $signature = ''; openssl_sign($payload, $signature, $key, OPENSSL_ALGO_SHA256); openssl_free_key($key); return base64_encode($signature); } } <?php namespace backend\components\helper; use common\helpers\MiniHelper; class WxDelivery { /** * Name:获取小程序的token * User: zcw * Date: 2023/7/11 * Time: 9:50 * @return bool|int|mixed|string */ public static function getAccessToken() { $qr = new MiniHelper(); // 获取到的 access_token return $qr->getAccessToken(); } public static function curlPost($url, $field,$header) { $headerArray = array("Content-type:application/json;charset=utf-8", "Accept:application/json"); $headerArray = array_merge($headerArray,$header); $curl = curl_init(); curl_setopt($curl, CURLOPT_HTTPHEADER, $headerArray); curl_setopt($curl, CURLOPT_URL, $url); curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); curl_setopt($curl, CURLOPT_POST, 1); curl_setopt($curl, CURLOPT_POSTFIELDS, $field); $str = curl_exec($curl); curl_close($curl); return $str; }
2023-07-12 - 同城配送接口错误,错误代码:934011,如何解决?
今天在接入同城配送的时候,接口报错{"errcode":934011,"errmsg":"request_body is required rid: 64acbde3-52b8d557-67db4a06"} 但是requestBody已经传入相关参数,我看接入文档里面的同学也有相关的反馈 [图片] [图片] 有没有官方的来解答一下呢
2023-07-11