天天看點

微信小程式 支付 - 紅包功能實作 附源碼( 簽名錯誤、CA憑證錯誤等解決辦法)

場景:小程式開發一個拆紅包的功能,背景thinkphp3.2,小程式和微信商戶平台不是同一個賬号,但是已經關聯

官方接口文檔:微信支付-企業付款到零錢

使用條件

1、商戶号(或同主體其他商戶号)已入駐90日

2、商戶号(或同主體其他商戶号)有30天連續正常交易

3、登入微信支付商戶平台-産品中心,開通企業付款。

4、紅包金額目前最新規定為 0.30元 到 200元(傳參的時候微信以分為機關,是以amount最小值30,最大值20000)

企業付款到零錢 接口連結:https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers

目前項目的小程式

各種坑不一一列舉,隻寫注意事項及容易出錯的地方:

注意事項:

1. 參數名嚴格按照官方文檔輸入(文後有代碼範例作為參考)

2. 小程式登入網址 https://mp.weixin.qq.com

3. CA憑證要用商戶号裡下載下傳的證書。商戶号登入網址:https://pay.weixin.qq.com(商戶登入賬号類似:[email protected],密碼在騰訊發的郵件裡有六位數字的)

4. mchid:商戶平台下的mchid

5. mch_appid: 小程式的appid

6. openid: 小程式的使用者openid

7. key: 微信商戶平台的密鑰key(sign簽名使用的密鑰)

另外:如果你用微信線上簽名生成的簽名跟你自己生成的簽名是一樣的,但是傳回還是報簽名錯誤,很有可能是以上4-7的參數用錯了,仔細檢查吧(小程式支付也一樣)

小程式拆紅包代碼:

//紅包js代碼
   onHongbao: function () {
      var that = this;
      if (that.hongbaoOpened){
         wx.showModal({
            title: '紅包已拆過',
            content: '紅包已拆過',
            showCancel: false,
         })
      }else{
         //拆紅包按鈕顯示控制
         that.setData({
            hongbaoOpened: true
         })
         //openid傳給背景,擷取紅包
         app.post('https://zhipur.com/api/pay/wxpay_hongbao',{
            openid: openid,
         }).then( (res)=>{
            console.log(res);//微信接口傳回資訊
            if(res.wxmsg.return_code=="SUCCESS"&&res.wxmsg.result_code=="SUCCESS"){
                //使用者界面顯示紅包資訊
                wx.showModal({
                    title: res.from + '發放的紅包',
                    content: res.amount + '元已轉入微信錢包',
                    showCancel: false,
                 })
            }else{
                //使用者界面顯示紅包資訊
                wx.showModal({
                    title: '紅包出錯了',
                    content: '紅包出錯了',
                    showCancel: false,
                 })
            } 
         }).catch( (err)=>{
            console.log(err);//微信接口傳回資訊 
         });  
      } 
   },

           

背景PHP代碼:

/**
     * 微信小程式紅包 企業付款
     * @param {String} $cid 訂單編号
     * @param {String} $name 訂單名
     * @param {Datetime} $subdate 送出日期
     */
    public function wxpay_hongbao(){

        $post = I('post.');

        // 是否已開啟過紅包
        // $this->hongbaoOpened($post['openid'], $post['itemId']);

        //讀取配置
        $cfg = C('WXPAY_CONFIG');

        $nonce_str = abs_rand();//生成16位随機字元串

        $api = $cfg['hongbao_api'];//微信企業付款到零錢接口

        //擷取IP 或者直接擷取$_SERVER['REMOTE_ADDR']
        $spbill_create_ip = $_SERVER['REMOTE_ADDR'] == '::1' ? '192.127.1.1' : $_SERVER['REMOTE_ADDR'];
        //微信随機金額紅包 範圍:30 - 2000分,預設:30 - 50分
        $min=;
        $max=;
        $amount = $this->make_wxhb_rand_money($min,$max);
        /** 微信接口 */
        $mchid = $cfg['mch_id'];//微信商戶平台 商戶号
        $appid = $cfg['mini_appid'];//小程式 appid
        $trade_key = $cfg['trade_key'];//微信商戶平台 密鑰
        $openid = $post['openid'] ? $post['openid'] : 'oVyb00JwH1J8tKdlUuZ4FZOsteVw';//預設openid回收錯誤openid的紅包資金
        $desc = '紅包';
        $partner_trade_no = date('Ymd').mt_rand(,) ;//生成訂單号 比如'2018032812345'
        $signArr = array(
                        'amount'=>$amount,
                        'check_name'=>'NO_CHECK',
                        'desc'=>$desc,
                        'mch_appid'=>$appid,
                        'mchid'=>$mchid,
                        'nonce_str'=>$nonce_str,
                        'openid'=>$openid,
                        'partner_trade_no'=>$partner_trade_no,
                        'spbill_create_ip'=>$spbill_create_ip,

            );

        //企業付款到零錢 簽名
        $sign = $this->make_wxhb_sign($signArr,$trade_key);

        $postXml = $this->array2xml($signArr, $sign);
        //curl 微信支付接口擷取 prepay_id
        $xmlRes = curl_post_xml_with_wxCA($api, $postXml);//微信傳回資料

        $postObj = xml2obj($xmlRes);//轉對象

        //如果成功,寫入資料庫hongbao表
        if($postObj->result_code=='SUCCESS'&&$postObj->return_code=='SUCCESS'){
            $this->doHongbao($amount, $openid, $post['cityName']);
            $this->ajaxReturn( array('wxmsg'=>$postObj, 'amount'=>($amount/), 'from'=>'貨驗金睛') );
        }else{
            $this->ajaxReturn( array('wxmsg'=>$postObj) );
        }
    }

    /**
     * 紅包成功後的業務邏輯
     * @param  {Array}    $arr   源數組
     * @param  {String}   $sign  簽名字元串
     * @return  {String}  $xml   xml字元串
     */
    protected function doHongbao($amount, $openid, $city='北京市朝陽區', $time){
        //未完成
    }

    /**
     * 改紅包是否已經被拆過
     * @param  {String}   $openid  小程式對應openid
     * @param  {String}   $itemid  物品id/紅包id
     * @return  {String}  $xml   xml字元串
     */
    protected function hongbaoOpened($openid, $itemid){
        //未完成
    }

    /**
     * 數組生成微信api适配的xml字元串
     * @param  {Array}    $arr   源數組
     * @param  {String}   $sign  簽名字元串
     * @return  {String}  $xml   xml字元串
     */
    protected function array2xml($arr,$sign){

        if(! is_array($arr) ) return false;
        $xml = '<xml>';
        foreach ($arr as $k => $v) {
            $xml .= '<'.$k.'>'.$v.'</'.$k.'>';
        }
        $xml .= '<sign>'.$sign.'</sign></xml>';
        return $xml;
    }
    /**
     * 生成随機紅包金額 - 小程式企業付款到零錢
     * @param  {Float}  $min   下限
     * @param  {Float}  $max   上限
     * @param  {Float}  $rnd   随機金額
     */
    protected function make_wxhb_rand_money($min=,$max=){

        return mt_rand($min,$max);
    }
    /**
     * 生成簽名 - 小程式企業付款到零錢
     * @param  {Array}   $arr   要傳遞的參數數組
     * @param  {Boolean} $is_call_back 是否二次簽名
     * @return {String}  $sign  簽名字元串
     */
    protected function make_wxhb_sign($arr,$trade_key,$is_call_back=false) {

        ksort($arr);        //将參數數組按照參數名ASCII碼從小到大排序
        reset($arr);

        foreach ($arr as $key => $value) {
            if($key!='sign' && $key!='sign_type'){
                $newArr[] = $key.'='.$value;     // 整合新的參數數組
            }

        }
        $stringA = implode("&", $newArr);         //使用 & 符号連接配接參數
        $stringSignTemp = $stringA."&key=".$trade_key;        //拼接key

        $sign = strtoupper( MD5($stringSignTemp) );//将字元串進行MD5加密 并轉換為大寫

        return $sign;
    }