记一次开发小程序红包的经历

        当时接到这个需求,瞬间就想到了微信的小程序红包,后来大体翻了一下文档,觉得就是给这个需求量身定做的.在第一个30天流水失败后,找了个微信商户的合作公司,坐快车,2天开通了微信小程序红包,微信企业付款到零钱功能.

        接下来就是开发了,主要是3个接口,发放红包,领取红包,和验证红包状态,4个步骤,发放红包,验证红包状态,领取红包,验证红包状态.这里是官方链接: https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=18_4&index=1

  1.         1. 发放红包,参数很多,我就不一一细说,可以看看官方的说明,我直接上代码

    1.             1) 为了方便请求,封装一个红包基类Red.php

      1. <?php

      2. namespace app\api\controller;

      3. /*

      4. * 微信支付:小程序红包接口

      5. */

      6. use think\Controller;

      7. class Red extends Controller

      8. {

      9.     private $sendurl = 'https://api.mch.weixin.qq.com/mmpaymkttransfers/sendminiprogramhb';//发放红包接口

      10.     private $searchurl = 'https://api.mch.weixin.qq.com/mmpaymkttransfers/gethbinfo';//红包查询接口

      11.     private $mch_id = '你的商户号';//商户号

      12.     private $wxappid = '你的公众账号appid';//公众账号appid

      13.     private $send_name = '随便写';//商户名称

      14.     private $total_num = 1;//红包发放总人数,默认都是1个

      15.     private $notify_way = 'MINI_PROGRAM_JSAPI';//通知用户形式,这里不用改

      16.     private $scene_id = '';//发放红包使用场景,红包金额大于200时必传

      17.     private $key='你的秘钥';//商户号支付钥匙


      18.     /**

        1.   * 发放红包

      19.      * @param $mch_billno

      20.      * @param $re_openid

      21.      * @param $total_amount

      22.      * @param $wishing

      23.      * @param $act_name

      24.      * @param $remark

      25.      * @return mixed

      26.      * @throws \Exception

      27.      */

      28.     public function sendhb($mch_billno,$re_openid,$total_amount,$wishing,$act_name,$remark)

      29.     {

      30.         //随机字符串

      31.         $nonce_str = $this->createNoncestr();

      32.         $parameters = array(

      33.             'nonce_str' => $nonce_str,

      34.             'mch_billno' => $mch_billno,

      35.             'mch_id' => $this->mch_id,

      36.             'wxappid' => $this->wxappid,

      37.             'send_name' => $this->send_name,

      38.             're_openid' => $re_openid,

      39.             'total_amount' => $total_amount,

      40.             'total_num' => $this->total_num,

      41.             'wishing' => $wishing,

      42.             'act_name' => $act_name,

      43.             'remark' => $remark,

      44.             'notify_way' => $this->notify_way,

      45.             'scene_id' => $this->scene_id

      46.         );

      47.         //生成签名,所有参数+key然后MD5

      48.         $parameters['sign'] = $this->getSign($parameters);

      49.         $xmlData = $this->arrayToXml($parameters);

      50.         $curlres = $this->postXmlCurl($xmlData, $this->sendurl);

      51.         $res = $this->xmlToArray($curlres);

      52.         return $res;

      53.     }


      54.     /**

      55.      * 根据订单号查询红包状态

      56.      * @param $mch_billno

      57.      * @param $mch_id

      58.      * @param $appid

      59.      * @return mixed

      60.      * @throws \Exception

      61.      */

      62.     public function searchhb($mch_billno,$mch_id,$appid)

      63.     {

      64.         //随机字符串

      65.         $nonce_str = $this->createNoncestr();

      66.         $parameters = array(

      67.             'nonce_str' => $nonce_str,

      68.             'mch_billno' => $mch_billno,

      69.             'mch_id' => $mch_id,

      70.             'appid' => $appid,

      71.             'bill_type' => 'MCHT'

      72.         );

      73.         //生成签名,所有参数+key然后MD5

      74.         $parameters['sign'] = $this->getSign($parameters);

      75.         $xmlData = $this->arrayToXml($parameters);

      76.         $curlres = $this->postXmlCurl($xmlData, $this->searchurl);

      77.         $res = $this->xmlToArray($curlres);

      78.         return $res;

      79.     }


      80.     private function postXmlCurl($xml, $url)

      81.     {

      82.         $ch = curl_init();

      83.         curl_setopt($ch, CURLOPT_URL, $url);

      84.         //设置header

      85.         curl_setopt($ch, CURLOPT_HEADER, FALSE);

      86.         //要求结果为字符串且输出到屏幕上

      87.         curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);

      88.         //post提交方式

      89.         curl_setopt($ch, CURLOPT_POST, TRUE);

      90.         curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);

      91.         curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 20);

      92.         curl_setopt($ch, CURLOPT_TIMEOUT, 20);

      93.         curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);

      94.         curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);

      95.         //证书的位置

      96.         curl_setopt($ch, CURLOPT_SSLCERT, APP_PATH . '/certRed/apiclient_cert.pem');    //你的证书所在路径

      97.         //证书key的位置

      98.         curl_setopt($ch, CURLOPT_SSLKEY, APP_PATH . '/certRed/apiclient_key.pem');    //你的证书key所在路径


      99.         //运行curl

      100.         $data = curl_exec($ch);

      101.         //返回结果

      102.         if ($data) {

      103.             curl_close($ch);

      104.             return $data;

      105.         } else {

      106.             $error = curl_errno($ch);

      107.             curl_close($ch);

      108.             throw new \Exception("curl出错,错误码:$error");

      109.         }

      110.     }


      111. //数组转换成xml

      112.     private function arrayToXml($arr)

      113.     {

      114.         $xml = "<xml>";

      115.         foreach ($arr as $key => $val) {

      116.             if (is_array($val)) {

      117.                 $xml .= "<" . $key . ">" . $this->arrayToXml($val) . "</" . $key . ">";

      118.             } else {

      119.                 $xml .= "<" . $key . ">" . $val . "</" . $key . ">";

      120.             }

      121.         }

      122.         $xml .= "</xml>";

      123.         return $xml;

      124.     }


      125.     //xml转换成数组

      126.     private function xmlToArray($xml)

      127.     {

      128.         //禁止引用外部xml实体

      129.         libxml_disable_entity_loader(true);

      130.         $xmlstring = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);

      131.         $val = json_decode(json_encode($xmlstring), true);

      132.         return $val;

      133.     }


      134.     //作用:产生随机字符串,不长于32位

      135.     public function createNoncestr($length = 32)

      136.     {

      137.         $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

      138.         $str = "";

      139.         for ($i = 0; $i < $length; $i++) {

      140.             $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);

      141.         }

      142.         return $str;

      143.     }


      144.     //作用:生成签名

      145.     public function getSign($Obj)

      146.     {

      147.         foreach ($Obj as $k => $v) {

      148.              //不为空的参数才参与签名

      149.             if (!empty($v)) {

      150.                 $Parameters[$k] = $v;

      151.             }

      152.         }

      153.         //签名步骤一:按字典序排序参数

      154.         ksort($Parameters);

      155.         $String = $this->formatBizQueryParaMap($Parameters, false);

      156.         //签名步骤二:在string后加入KEY

      157.         $String = $String . "&key=" . $this->key;

      158.         //签名步骤三:MD5加密

      159.         $String = md5($String);

      160.          //签名步骤四:所有字符转为大写

      161.         $result = strtoupper($String);

      162.         return $result;

      163.     }


      164.      //作用:格式化参数,签名过程需要使用

      165.     private function formatBizQueryParaMap($paraMap, $urlencode)

      166.     {

      167.         $buff = "";

      168.         ksort($paraMap);

      169.         foreach ($paraMap as $k => $v) {

      170.             if ($urlencode) {

      171.                 $v = urlencode($v);

      172.             }

      173.             $buff .= $k . "=" . $v . "&";

      174.         }

      175.         $reqPar = '';

      176.         if (strlen($buff) > 0) {

      177.             $reqPar = substr($buff, 0, strlen($buff) - 1);

      178.         }

      179.         return $reqPar;

      180.     }

      181. }


                  2). 在另外一个控制器,写个测试方法调用接口

        1. /**

      1.      *  测试红包发放/红包查询接口

      2.      * @param string $mch_billno  订单号

      3.      * @param string $mch_id      商户号

      4.      * @param string $appid       appid

      5.      * @throws \Exception

      6.      */

      7.     public function test($mch_billno='',$mch_id='',$appid='')

      8.     {

      9.         if ($mch_billno && $mch_id) {   //查询红包状态

      10.             $weixinpay = new Red();

      11.             $res = $weixinpay->searchhb($mch_billno, $mch_id, $appid);

      12.             print_r($res);

      13.         } else {     //发放红包

      14.             $money = mt_rand(100, 999);   //红包金额,测试金额1元~9.99元

      15.             $mch_billno = date("YmdHis") . rand(100, 999);//商户订单号

      16.             $re_openid = $this->openid;//用户openid

      17.             $total_amount = $money;//付款金额,单位分

      18.             $wishing = '恭喜';//红包祝福语

      19.             $act_name = '奖励';//活动名称

      20.             $remark = '您的专属红包';//备注

      21.             $weixinpay = new Red();

      22.             $res = $weixinpay->sendhb($mch_billno, $re_openid, $total_amount, $wishing, $act_name, $remark);

      23.             print_r($res);

      24.         }

      25.     }

                  3). 测试红包发放接口,尴尬,余额不足.这里提醒一下,刚开通小程序红包的朋友,一定要进行产品设置,限额限数限ip,我改一下金额再跑一下

                      image.png

                          第二次发放成功了,说明发放接口没问题

                          image.png

                  4). 测试红包验证接口,这次很顺利,请求成功,status对照文档看

                          image.png

    2.   2. 领取红包,这个动作要在小程序中调用,还是直接上代码

      1. 1). 这里我将领取红包的参数在方法里处理好,用接口返回到小程序

        1. $arr['appId'] = '你的appid';

        2. $arr['timeStamp'] = '"'.time().'"';    //时间戳要转字符串格式

        3. $arr['package'] = 'urlencode之后的package,这个参数在红包发放成功之后可获取';   //package要进行urlencode

        4. $weixinpay = new Red();

        5. $arr['nonceStr'] = $weixinpay->createNoncestr();

        6. $arr['paySign'] = $weixinpay->getSign($arr);

          $arr['signType'] = 'MD5';   //不参与签名加密

          $this->success('发送成功',$arr);

               2). 小程序端接口接收参数

                     //后端传参,前端调用sendBizRedPacket函数

                    httpGet("/api/web/getRed", function(res) {

                      if (res.code == 1) {

                        console.log('拆红包');

                        wx. sendBizRedPacket ({

                              timeStamp: res.data.timeStamp, // 支付签名时间戳,

                              nonceStr: res.data.nonceStr, // 支付签名随机串,不长于 32 位

                              package: res.data.package, //扩展字段,由商户传入

                              signType: res.data.signType, // 签名方式,

                              paySign: res.data.paySign, // 支付签名

                              success:function(res){

                                console.log('成功',res);

                              },

                              fail:function(res){

                                console.log('失败',res);

                              },

                              complete:function(res){

                                console.log('结束',res);

                              }

                         })

                        console.log('拆红包之后');

                      }

                    })

                3). 查看执行结果

                     image.png

               4). gg,彩蛋时间到了,场景值不支持.有没有想到什么,目前小程序红包仅支持用户微信扫码打开小程序,进行红包领取

                   image.png

          我是gg了,我们的业务要求是用户发布信息成功之后,触发红包发放功能.如果你的需求是营销拉新,扫码触发.恭喜你,直接复制粘贴吧,记得改参数

发表评论