Laravel 微信小程序支付
2018-04-04 16:12
239 查看
近期项目,有一个涉及到微信小程序支付功能,踩了不少坑,记录一哈子。
开发环境:php 7.0.12 + Apache
框架:Laravel5.3
微信官方的流程示意图:
作为phper,要做的部分就是用前端传递过来的code换取openid,生成商户订单,再调用支付统一下单API换取预付单信息,将预付单信息再次签名后返回给前端。
code换取openid:
获取随机字符串:
xml与array转换:
生成签名:
curl发送请求:
预支付:
业务逻辑部分:
PS:
小程序的appid与secret与公众号的是不同的;
请确保各项配置无误,我会说因为需求方给的key错误耽误了一天时间ヽ(`Д´)ノ︵ ┻━┻ ┻━┻;
一般错误都会告知原因,个人猜测因为key比较私密所以因为key错误,返回的讯息一直都是 “签名错误”,并未给出具体原因;
微信提供了签名校验,可以设置好参数去校验下自己的签名生成算法是否OK;
生成预支付订单后,返回给前端的时候需要再次签名,切记.
参考链接:
微信小程序支付流程
统一支付下单API参数
开发环境:php 7.0.12 + Apache
框架:Laravel5.3
微信官方的流程示意图:
作为phper,要做的部分就是用前端传递过来的code换取openid,生成商户订单,再调用支付统一下单API换取预付单信息,将预付单信息再次签名后返回给前端。
code换取openid:
/** * code换取openid * @param $code * @return bool */ function getOpenID($code) { //从配置文件读取小程序的appid&secret $appid = config('miniapp_id'); $secret = config('mini_secret'); $url = "https://api.weixin.qq.com/sns/jscode2session?appid=$appid&secret=$secret&js_code=$code&grant_type=authorization_code"; $weixin = file_get_contents($url);//通过code换取网页授权access_token $jsondecode = json_decode($weixin); //对JSON格式的字符串进行编码 $array = get_object_vars($jsondecode);//转换成数组 if (!isset($array['openid'])) { throw new \Exception('code错误T^T'); } $openid = $array['openid'];//输出openid return $openid; }
获取随机字符串:
/** * 产生随机字符串,不长于32位 * @param int $length * @return string 产生的随机字符串 */ 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; }
xml与array转换:
/** * 将一个数组转换为 XML 结构的字符串 * @param array $arr 要转换的数组 * @param int $level 节点层级, 1 为 Root. * @return string XML 结构的字符串 */ function arraytoXml($arr, $level = 1) { $s = $level == 1 ? "<xml>" : ''; foreach ($arr as $tagname => $value) { if (is_numeric($tagname)) { $tagname = $value['TagName']; unset($value['TagName']); } if (!is_array($value)) { $s .= "<{$tagname}>" . (!is_numeric($value) ? '<![CDATA[' : '') . $value . (!is_numeric($value) ? ']]>' : '') . "</{$tagname}>"; } else { $s .= "<{$tagname}>" . $this->arraytoXml($value, $level + 1) . "</{$tagname}>"; } } $s = preg_replace("/([\x01-\x08\x0b-\x0c\x0e-\x1f])+/", ' ', $s); return $level == 1 ? $s . "</xml>" : $s; } /** * 将xml转为array * @param string $xml xml字符串 * @return array 转换得到的数组 */ function xmltoArray($xml) { //禁止引用外部xml实体 libxml_disable_entity_loader(true); $result = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true); 4000 return $result; }
生成签名:
/** * 生成签名 * @param $data * @return string */ function makeSign($data) { //获取微信支付秘钥 $key = config('key'); //去空 $data = array_filter($data); //签名步骤一:按字典序排序参数 ksort($data); $string_a = http_build_query($data); $string_a = urldecode($string_a); //签名步骤二:在string后加入KEY $string_sign_temp = $string_a . "&key=$key"; //签名步骤三:MD5加密 $sign = md5($string_sign_temp); //签名步骤四:所有字符转为大写 return strtoupper($sign); }
curl发送请求:
/** * 微信支付发起请求 * @param $url * @param $xmldata * @param int $second * @param array $aHeader * @return bool|mixed */ protected function curl_post_ssl($url, $xmldata, $second = 30, $aHeader = array()) { $ch = curl_init(); //超时时间 curl_setopt($ch, CURLOPT_TIMEOUT, $second); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //这里设置代理,如果有的话 //curl_setopt($ch,CURLOPT_PROXY, '10.206.30.98'); //curl_setopt($ch,CURLOPT_PROXYPORT, 8080); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); if (count($aHeader) >= 1) { curl_setopt($ch, CURLOPT_HTTPHEADER, $aHeader); } curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $xmldata); $data = curl_exec($ch); if ($data) { curl_close($ch); return $data; } else { $error = curl_errno($ch); echo "call faild, errorCode:$error\n"; curl_close($ch); return false; } }
预支付:
/** * @param $unifiedorder * @return array * @throws \Exception */ function prepay($unifiedorder) { $unifiedorder['sign'] = $this->makeSign($unifiedorder); $xmldata = $this->arraytoXml($unifiedorder); $url = config('pay_url'); $res = $this->curl_post_ssl($url, $xmldata); if (!$res) { throw new \Exception('链接Wechat服务器失败 (キ`゚Д゚´)'); } $content = $this->xmltoArray($res); if (strval($content['return_code']) == 'FAIL') { throw new \Exception('生成签名数据失败 ( ̄□ ̄;)'); } //拼接小程序的接口数据 $result = [ 'appId' => strval($content['appid']), 'timeStamp' => time(), 'nonceStr' => $this->getNonceStr(), 'package' => 'prepay_id=' . strval($content['prepay_id']), 'signType' => 'MD5', ]; //加密签名 $result['paySign'] = $this->makeSign($resData); return $result; }
业务逻辑部分:
function WeChatOrder(Request $request) { $code = $request->get('code'); $payHelper = new WechatHelper(); $openId = $payHelper->getOpenID($code); try { DB::beginTransaction(); //生成业务订单 $order = [ 'orderNum' => 123456789, 'price' => 2333, ... ]; Order::create($order); $unifiedorder = [ 'openid' => $openId, 'appid' => config('miniapp_id'), 'mch_id' => config('mch_id'), 'nonce_str' => $payHelper->getNonceStr(),//获取随机字符串 'body' => '商品讯息', 'out_trade_no' => $order['orderNum'], 'total_fee' => $order['price'],//单位为"分" 'spbill_create_ip' => $request->ip(), 'notify_url' => config('notify_url'), 'trade_type' => 'JSAPI',//小程序均为"JSAPI" ]; //再次签名返回 $signature = $payHelper->prepay($unifiedorder); DB::commit(); } catch (QueryException $e) { DB::rollback(); throw new \Exception('提交订单失败 T^T'); } catch (\Exception $e) { DB::rollback(); throw new \Exception('出错了 T^T'); } //返回$signature给前端 }
PS:
小程序的appid与secret与公众号的是不同的;
请确保各项配置无误,我会说因为需求方给的key错误耽误了一天时间ヽ(`Д´)ノ︵ ┻━┻ ┻━┻;
一般错误都会告知原因,个人猜测因为key比较私密所以因为key错误,返回的讯息一直都是 “签名错误”,并未给出具体原因;
微信提供了签名校验,可以设置好参数去校验下自己的签名生成算法是否OK;
生成预支付订单后,返回给前端的时候需要再次签名,切记.
参考链接:
微信小程序支付流程
统一支付下单API参数
相关文章推荐
- 小程序支付demo,微信小程序支付php代码
- 微信小程序 调用支付jsapi时缺少参数:total_fee
- 微信小程序支付之多次支付
- webview打开微信支付,web view h5页面中调用小程序支付——小程序web-view高级用法11
- 微信小程序支付
- 微信小程序支付前端源码
- 微信小程序的支付和退款流程
- 小程序支付授权目录,微信小程序需要小程序支付授权吗
- 微信小程序支付(java后端)
- 微信小程序支付及退款流程详解
- 微信小程序支付及退款流程详解
- 微信小程序开发之webview组件内网页实现微信原生支付
- 微信小程序支付失败 签名失败及签名错误的解析 -- KeeSoft框架
- 微信小程序支付PHP代码
- 微信小程序支付C#后端源码
- 微信小程序支付
- 微信小程序支付功能 C# .NET开发
- 小程序模板源码下载,微信小程序企业模板,支付,外卖,商城模板源码下载
- 微信小程序支付功能后台PHP实现
- 微信小程序前台的用户数据入库(后台Laravel)