目录 一、首先我们来填个坑 1:支付验签失败 二、代码示例 1.请求参数配置 2.统一下单API 3.MakeSign 签名 4.ToXml 数组参数转xml 5.postXmlCurl 发送请求 6.FromXml 结果xml参数转数组
目录
这个问题折磨了我两天,官方文档比较含糊不清。各种百度下来的方法试过之后也不尽人意,最后发现问题是没有二次签名
二次签名需要参数(代码会展示在哪里二次签名):
appId: 商户申请的公众号对应的appid(I大写)
nonceStr: 随机字符串(注意是jsapi下单接口中返回的 nonce_str、不是重新生成)
package: 统一下单接口返回的prepay_id参数值 ,(注意格式prepay_id=wx.....)
signType: 签名类型、(官方文档)仅支持RSA。
(我的签名类型是 HMac-SHA256 也是可以的,必须和下单使用的签名类型保持一致)
timeStamp:时间戳(这里要把 time() 转成字符串类型)
注明:使用这五个参数生成的 paySign 签名才是需要返给前端的(
)
官方文档实例要计算签名也给我整的蒙圈,最后发现直接将五个必须参数生成的签名返给前端就可以直接调取API了
$oInput = ['body' => '测试商品', // 商品说明 'attach' => '测试场景', // 自定义参数:可以用来做回调后场景区分 'out_trade_no' => '测试单号' . time(), // 自定义订单号 'total_fee' => 1 * 100, // 付款金额:记得*100 微信官方是以分为单位 'goods_tag' => '', // 优惠券相关参数 'notify_url' => 'http://...', // 回调通知地址'trade_type' => 'JSAPI', // 支付方式 'openid' => $openid, // 付款用户openid ];$res = $this->unifiedOrder($oInput); // 这里我调用的统一下单return $res; // 返给前端带APPID等参数给前端去调用支付
const SSLCERT_PATH = 'Cert证书路径'; const SSLKEY_PATH = 'Key证书路径'; const MCHID = '商户号'; const APPID = 'APPID'; const KEY = '商户支付密钥(必须配置,登录商户平台自行设置)'; public function unifiedOrder($inputObj, $timeOut = 6){$url = "https://api.mch.weixin.qq.com/pay/unifiedorder";// 首次签名参数$oValues = ['body' => $inputObj['body'],// 设置商品或支付单简要描述'attach' => $inputObj['attach'],// 设置附加数据,用于商户携带订单的自定义数据'out_trade_no' => $inputObj['out_trade_no'], // 设置商户系统内部的订单号,transaction_id、out_trade_no二选一,如果同时存在优先级:transaction_id> out_trade_no'total_fee' => $inputObj['total_fee'], // 设置订单总金额,只能为整数,单位:分'time_start' => date("YmdHis"), // 设置订单生成时间'time_expire' => date("YmdHis", time() + 600), // 设置订单失效时间'goods_tag' => $inputObj['goods_tag'], // 设置商品标记,代金券或立减优惠功能的参数'notify_url' => $inputObj['notify_url'], // 获取接收微信支付异步通知回调地址的值'trade_type' => $inputObj['trade_type'], // JSAPI,NATIVE,APP'openid' => $inputObj['openid'], // 用户在商户appid下的唯一标识'appid' => self::APPID, // 顶部定义'mch_id' => self::MCHID, // 顶部定义'spbill_create_ip' => $_SERVER['REMOTE_ADDR'], // 终端ip'nonce_str' => $this->getNonceStr(), // 随机32位字符串'sign_type' => 'HMAC-SHA256', // 签名类型,自行替换];// 首次签名ksort($oValues);$oValues['sign'] = $this->MakeSign($oValues); // 调用签名$xml = $this->ToXml($oValues); // 数字转xml类型$response = self::postXmlCurl($xml, $url, false, $timeOut); // 请求$result = $this->FromXml($response); // 请求结果从xml转成数组类型 // 二次签名参数$oResult = ['appId' => $result['appid'], // 首次请求中的appid'nonceStr' => $result['nonce_str'], // 首次请求中的nonce_str'package' => 'prepay_id=' . $result['prepay_id'],// 首次请求中的prepay_id'signType' => 'HMAC-SHA256', // 跟首次签名中的签名类型参数保持一致'timeStamp' => (string)(time()),// 时间戳转字符串类型]; // 二次签名$oResult['paySign'] = $this->MakeSign($oResult); // 调用签名$result = json_encode($oResult); // encode数组return $result; // 直接返回}public function MakeSign($values, $needSignType = true){if ($needSignType) {$sSignType = 'HMAC-SHA256'; // 可以在文档开头用枚举定义: 所有签名类型必须一致}$sKey = self:KEY; // KEY:商户支付密钥(必须配置,登录商户平台自行设置)// 签名步骤一:按字典序排序参数ksort($values);$string = $this->ToUrlParams($values);// 签名步骤二:在string后加入KEY$string = $string . "&key=" . $sKey;// 签名步骤三:MD5加密或者HMAC-SHA256if ($sSignType == "MD5") {$string = md5($string);} else if ($sSignType == "HMAC-SHA256") {$string = hash_hmac("sha256", $string, $sKey);} else {return "签名类型不支持!";}// 签名步骤四:所有字符转为大写$result = strtoupper($string);return $result;} public function ToXml($values){if (!is_array($values) || count($values) <= 0) {return "数组数据异常!";}$xml = "";foreach ($values as $key => $val) {if (is_numeric($val)) {$xml .= "<" . $key . ">" . $val . "" . $key . ">";} else {$xml .= "<" . $key . ">" . $key . ">";}}$xml .= " ";return $xml;}private function postXmlCurl($xml, $url, $useCert = false, $second = 30){$ch = curl_init();$curlVersion = curl_version();$ua = "WXPaySDK/" . self::VERSION . " (" . PHP_OS . ") php/" . PHP_VERSION . " CURL/" . $curlVersion['version'] . " " . $aWxpayParam['mchid'];//设置超时curl_setopt($ch, CURLOPT_TIMEOUT, $second);$proxyHost = "0.0.0.0";$proxyPort = 0;// 如果有配置代理这里就设置代理if ($proxyHost != "0.0.0.0" && $proxyPort != 0) {curl_setopt($ch, CURLOPT_PROXY, $proxyHost);curl_setopt($ch, CURLOPT_PROXYPORT, $proxyPort);}curl_setopt($ch, CURLOPT_URL, $url);// curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,TRUE);// curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);//严格校验curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); //严格校验curl_setopt($ch, CURLOPT_USERAGENT, $ua);// 设置headercurl_setopt($ch, CURLOPT_HEADER, FALSE);// 要求结果为字符串且输出到屏幕上curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);if ($useCert == true) {// 设置证书// 使用证书:cert 与 key 分别属于两个.pem文件// 证书文件请放入服务器的非WEB目录下$sslCertPath = self::SSLCERT_PATH ; // Cert证书路径:文档顶部自定义参数名$sslKeyPath = self::SSLKEY_PATH ; // Key证书路径:文档顶部自定义参数名curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');curl_setopt($ch, CURLOPT_SSLCERT, $sslCertPath);curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');curl_setopt($ch, CURLOPT_SSLKEY, $sslKeyPath);}// post提交方式curl_setopt($ch, CURLOPT_POST, TRUE);curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);// 运行curl$data = curl_exec($ch);// 返回结果if ($data) {curl_close($ch);return $data;} else {$error = curl_errno($ch);curl_close($ch);throw new WxPayException("curl出错,错误码:$error");}}public function FromXml($xml){if (!$xml) {return "xml数据异常!";}//将XML转为array//禁止引用外部xml实体libxml_disable_entity_loader(true);$res = JSON_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);return $res;}public function getNonceStr($length = 32){$chars = "abcdefghijklmnopqrstuvwxyz0123456789";$str = "";for ($i = 0; $i < $length; $i++) {$str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);}return $str;}public function ToUrlParams($urlObj){$buff = "";foreach ($urlObj as $k => $v) {if ($k != "sign" && $v != "" && !is_array($v)) {$buff .= $k . "=" . $v . "&";}}$buff = trim($buff, "&");return $buff;}
来源地址:https://blog.csdn.net/arlene12345/article/details/127831004
--结束END--
本文标题: PHP 实现微信支付 JSAPI
本文链接: https://www.lsjlt.com/news/404950.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
下载Word文档到电脑,方便收藏和打印~
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0