首页 > 技术文章 > 公众号给用户发红包

bilberry 2020-08-01 15:04 原文

场景:公众号里面,通过指定openid给用户发送红包,比如用户在公众号中提现,发福利红包都可以使用。

直接上代码:

class wxHongbao
{
    // 商户id
    private $mch_id = '********';

    // 订单号
    private $mch_billno = '';

    // 公众号id
    private $wxappid = '';

    // 调用接口机器ip
    private $client_ip = '';

    // 商户平台密钥
    private $apikey = '';

    // 红包发放人数
    private $total_num = 1;

    // 红包发送者名称
    private $send_name = '正义的程序猿';

    // 祝福语
    private $wishing = '欢迎再次参与';

    // 活动名称
    private $act_name = '';

    // 备注
    private $remark = '';

    // 随机字符串
    private $nonce_str = '';

    // 接收用户的openid
    private $re_openid = '';

    //红包金额,单位 分
    private $total_amount = 1;

    // 签名
    private $sign;

    /**
     * 活动信息
     * @var array
     */
    private $risk_info;

    // 发放红包使用场景,红包金额大于200或者小于1元时必传
    // PRODUCT_1:商品促销
    // PRODUCT_2:抽奖
    // PRODUCT_3:虚拟物品兑奖
    // PRODUCT_4:企业内部福利
    // PRODUCT_5:渠道分润
    // PRODUCT_6:保险回馈
    // PRODUCT_7:彩票派奖
    // PRODUCT_8:税务刮奖
    private $scene_id;

    // 证书
    private $apiclient_cert;
    private $apiclient_key;
    private $apiclient_ca;

    private $api_hb_single = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack";

    /**
     * wxHongbao constructor.
     */
    public function __construct()
    {
        // todo 初始化证书目录
        $this->apiclient_cert = '';
        $this->apiclient_key  = '';
        $this->apiclient_ca   = '';
    }

    /**
     * @return string
     */
    public function getSendName()
    {
        return $this->send_name;
    }

    /**
     * @param string $send_name
     * @return $this
     */
    public function setSendName($send_name)
    {
        $this->send_name = $send_name;
        return $this;
    }

    /**
     * @return string
     */
    public function getWishing()
    {
        return $this->wishing;
    }

    /**
     * @param string $wishing
     * @return $this
     */
    public function setWishing($wishing)
    {
        $this->wishing = $wishing;
        return $this;
    }

    /**
     * @return string
     */
    public function getActName()
    {
        return $this->act_name;
    }

    /**
     * @param string $act_name
     * @return $this
     */
    public function setActName($act_name)
    {
        $this->act_name = $act_name;
        return $this;
    }

    /**
     * @return string
     */
    public function getRemark()
    {
        return $this->remark;
    }

    /**
     * @param string $remark
     * @return  $this
     */
    public function setRemark($remark)
    {
        $this->remark = $remark;
        return $this;
    }

    /**
     * @return string
     */
    public function getReOpenid()
    {
        return $this->re_openid;
    }

    /**
     * @param string $re_openid
     * @return $this
     */
    public function setReOpenid($re_openid)
    {
        $this->re_openid = $re_openid;
        return $this;
    }

    /**
     * @return int
     */
    public function getTotalAmount()
    {
        return $this->total_amount;
    }

    /**
     * 红包金额,单位 分
     * @param int $total_amount
     * @return $this
     */
    public function setTotalAmount($total_amount)
    {
        $this->total_amount = (int)$total_amount;
        return $this;
    }

    /**
     * 创建红包
     * @param $openid string
     * @param $amount int 红包金额,单位分
     * @return $this
     */
    public function createHongbao($openid, $amount)
    {
        if (! $openid) {
            throw new \InvalidArgumentException('openid error');
        }

        if ($amount <= 0) {
            throw new \InvalidArgumentException('amount error');
        }

        $this->generate_nonce_str();
        $this->generate_mch_billno();
        $this->setReOpenid($openid);
        $this->setTotalAmount($amount);

        return $this;
    }

    /**
     * 发送红包
     * @return bool
     */
    public function send()
    {
        $this->generate_sign();

        $data = $this->generate_xml_params();

        $data_obj = $this->https_request($this->api_hb_single, $data);

        if ($data_obj->return_code === 'SUCCESS' && $data_obj->result_code === 'SUCCESS') {
            $log = json_encode((array)$data_obj);
            error_log(sprintf("[%s]\twx hongbao send success.\tmch_billno:%s\tret:%s", date("Y-m-d H:i:s"), $this->mch_billno, $log));

            return true;
        }

        $log = json_encode((array)$data_obj);
        error_log(sprintf("[%s]\twx hongbao send failed.\tmch_billno:%s\tret:%s", date("Y-m-d H:i:s"), $this->mch_billno, $log));

        return false;
    }

    private function generate_xml_params()
    {
        $xml = "<xml>
                <sign><![CDATA[{$this->sign}]]></sign>
                <mch_billno><![CDATA[{$this->mch_billno}]]></mch_billno>
                <mch_id><![CDATA[{$this->mch_id}]]></mch_id>
                <wxappid><![CDATA[{$this->wxappid}]]></wxappid>
                <send_name><![CDATA[{$this->send_name}]]></send_name>
                <re_openid><![CDATA[{$this->re_openid}]]></re_openid>
                <total_amount><![CDATA[{$this->total_amount}]]></total_amount>
                <total_num><![CDATA[{$this->total_num}]]></total_num>
                <wishing><![CDATA[{$this->wishing}]]></wishing>
                <client_ip><![CDATA[{$this->client_ip}]]></client_ip>
                <act_name><![CDATA[{$this->act_name}]]></act_name>
                <remark><![CDATA[{$this->remark}]]></remark>
                <nonce_str><![CDATA[{$this->nonce_str}]]></nonce_str>";

        if ($this->scene_id) {
            $xml .= "<scene_id><![CDATA[{$this->scene_id}]]></scene_id>";
        }

        if ($this->risk_info) {
            $rsk = http_build_query($this->risk_info);
            $rsk = urlencode($rsk);
            $xml .= "<risk_info>{$rsk}</risk_info>";
        }

        $xml .= "</xml>";

        return $xml;
    }

    // 生成签名
    private function generate_sign()
    {
        $params = array(
            'nonce_str' => $this->nonce_str,
            'mch_billno'=> $this->mch_billno,
            'mch_id'    => $this->mch_id,
            'wxappid'   => $this->wxappid,
            'send_name' => $this->send_name,
            're_openid' => $this->re_openid,
            'total_amount' => $this->total_amount,
            'total_num' => $this->total_num,
            'wishing'   => $this->wishing,
            'client_ip' => $this->client_ip,
            'act_name'  => $this->act_name,
            'remark'    => $this->remark
        );

        if ($this->scene_id) {
            $params['scene_id'] = $this->scene_id;
        }

        if ($this->risk_info) {
            $rsk = http_build_query($this->risk_info);
            $params['risk_info'] = urlencode($rsk);
        }

        ksort($params);

        $params['key'] = $this->apikey;

        $s = http_build_query($params);

        $this->sign = strtoupper(md5($s));
    }


    // 随机字符串
    private function generate_nonce_str($length = 16)
    {
        $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
        $chars_len = strlen($chars);
        $str = '';
        for ($i = 0; $i < $length; $i++) {
            $str .= $chars[mt_rand(0, $chars_len - 1)];
        }

        $this->nonce_str = $str;
    }

    // 订单号
    private function generate_mch_billno()
    {
        $s = substr($this->getReOpenid(), -4);

        $this->mch_billno = date('YmdHis') . $s;
    }

    private function https_request($url, $data = null)
    {
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
//        curl_setopt($curl, CURLOPT_TIMEOUT, 1);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
        curl_setopt($curl, CURLOPT_SSLCERT, $this->apiclient_cert);
        curl_setopt($curl, CURLOPT_SSLKEY, $this->apiclient_key);
        curl_setopt($curl, CURLOPT_CAINFO, $this->apiclient_ca);

        if (!empty($data)){
            curl_setopt($curl, CURLOPT_POST, 1);
            curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
        }
        $data = curl_exec($curl);
        curl_close($curl);

        return simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA);
    }
}

代码中引入这个类,需要配置一下商户id$mch_id,关联的公众号$wxappid,服务器的公网ip$client_ip,还有$apikey,然后在构造函数中配置下证书,就能直接用了:

$hongbao = new wxHongbao();
$hongbao->createHongbao('xxxx', 100)->send();

Happy Coding~

推荐阅读