微信支付
微信官方微信支付产品有付款码支付,JSAPI支付,Native支付,App支付,H5支付,小程序支付,人脸支付等不同的支付产品,在这里我们讲解微信小程序支付
- 业务流程
首先我们先看一下微信官方小程序支付文档所罗列的支付业务流程时序图
1、微信小程序用户进入产品详情页点击支付按钮
2、调用小程序登陆API,返回用户openid
3、商户系统处理订单业务
4、调用微信统一下单API
5、将微信返回的prepay_id配合其他数据再次签名,返回给前端
6、前段用返回的参数,调用微信支付,返回支付结果
7、微信异步通知商户支付结果
代码示例
<?phpnamespace AppHttpControllersapi;class WeChatPay{ protected $app_id; //小程序 app id protected $app_secret;//小程序的 secret protected $mch_id;//小程序商户号 protected $key;//商户号 key protected $notify_url;//支付回调地址 protected $pay_url;//统一下单请求地址 protected $trade_type;//支付交易类型 protected $order; protected $sign_type; protected $refund_url; public function __construct($order) { $this->app_id = ''; $this->app_secret = ''; $this->key = ''; $this->mch_id = ''; $this->notify_url = ''; $this->pay_url = 'https://api.mch.weixin.qq.com/pay/unifiedorder'; $this->refund_url = 'https://api.mch.weixin.qq.com/secapi/pay/refund'; $this->trade_type = 'JSAPI'; $this->order = $order; } /** * 微信对外支付方法 */ public function pay() { //统一下单 返回与支付信息 $pre_pay_id = $this->unifiedOrder($this->order); //再次签名 $package = $this->signAgain($pre_pay_id); return $package; } //微信统一下单 private function unifiedOrder($order) { $post_data = $this->getPayData($order); $wx_return_data = $this->httpCurl($post_data, $this->pay_url); $wx_return_data = $this->xmlToArray($wx_return_data); if ($wx_return_data['return_code'] == 'SUCCESS' && $wx_return_data['result_code'] == 'SUCCESS') { $pre_pay_id = $wx_return_data['prepay_id']; return $pre_pay_id; } //记录错误信息或者抛出异常 Log::error('统一下单失败,微信返回信息:' . json_encode($wx_return_data)); throw new Exception($wx_return_data['return_msg'], 401); } //根据预下单id再次签名 private function getPayData($order) { $params = [ 'appid' => $this->app_id, 'mch_id' => $this->mch_id, 'nonce_str' => $this->createNoncestr(), 'sign_type' => $this->sign_type, 'body' => $order['pro_desc'], 'attach' => $order['attach'], //此处传入的 attach,在回调的时候会原样返回 'out_trade_no' => $order['order_num'], 'fee_type' => 'CNY', 'total_fee' => $order['total_fee'], //单位为分 'spbill_create_ip' => $_SERVER['REMOTE_ADDR'],//终端ip 'notify_url' => $this->notify_url, 'trade_type' => $this->trade_type, 'openid' => $order['openid'], //交易类型为JSAPI时,必传 ]; $params['sign'] = $this->makeSign($params); $data = $this->arrayToXml($params); return $data; } /** * 创建32位随机字符串 * @param int $length * @return string */ private function createNoncestr($length = 32) { $chars = "abcdefghijklmnopqrstuvwxyz0123456789"; $str = ""; for ($i = 0; $i < $length; $i++) { $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1); } return $str; } /** * 获取签名 * @param $params array 所有发送的数据集合 * @return string string 签名信息 */ private function makeSign($params) { ksort($params); $string = $this->formatIntoQueryStr($params); $string = strtoupper(md5($string . "&key=" . $this->key)); return $string; } /** * 格式化成查询字符串 * @param $params * @return string */ private function formatIntoQueryStr($params) { $tmp_array = []; foreach ($params as $key => $value) { if (empty($value)) continue; $tmp_array[] = $key . '=' . $value; } $string = implode("&", $tmp_array); unset($tmp_array); return $string; } /** * 将数组转换为xml字符串 * @param $arg array * @return bool|string */ private function arrayToXml($arg) { if (!is_array($arg) || count($arg) == 0) return false; $xml = "<xml>"; foreach ($arg as $key => $val) { if (is_numeric($val)) { $xml .= "<" . $key . ">" . $val . "</" . $key . ">"; } else { $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">"; } } $xml .= "</xml>"; return $xml; } private function httpCurl($xml, $url,$is_need_pem = false, $second = 30, $headers = []) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); //设置header if (













