2018-02-06 13:38:20 BuFanQi_Info 阅读数 1715
  • 微信公众号开发7-用户管理-微信开发php

    微信公众平台开发之微信用户开发管理是子恒老师《微信公众平台开发》视频教程的第7部。详细讲解了用php开发微信,对微信公众平台中的粉丝用户管理开发。内容包含微信公众平台用户分组,获取微信用户列表,查询用户详情等等。欢迎反馈,微信/QQ:68183131

    14030 人正在学习 去看看 秦子恒

#简介
现金红包,是微信支付商户平台提供的营销工具之一,上线以来深受广大商户与用户的喜爱。商户可以通过本平台向微信支付用户发放现金红包。用户领取红包后,资金到达用户微信支付零钱账户,和零钱包的其他资金有一样的使用出口;若用户未领取,资金将会在24小时后退回商户的微信支付账户中。

微信支付api文档
PS:此功能需要用户申请微信商家支付,下载支付证书。

package com.lh.wx.service;

import okhttp3.*;
import okhttp3.internal.platform.Platform;
import org.apache.http.ssl.SSLContexts;

import com.lh.utils.common.DateUtils;
import com.lh.wx.model.SendRedPack;
import com.lh.wx.utils.Tool;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.*;
import java.security.cert.CertificateException;
import java.util.Date;
import java.util.TreeMap;


public class WeChatSendRedPack {


    public static void main(String[] args) throws Exception {
    	 String nonce_str = System.currentTimeMillis() + "";    
         // 设置订单号
  	   	String orderid ="1325860101"
  	   			+DateUtils.format(new Date(), "yyyyMMddHHmmss");

        //具体参数查看具体实体类,实体类中的的参数参考微信的红包发放接口,这里你直接用map,进行设置参数也可以。。。
        SendRedPack sendRedPack = new SendRedPack(
        		nonce_str,
        		orderid,
                "商户号",
                "公众号appid",
                "蓝海",
                "用户Openid",
                100,
                1,
                "恭喜发财",
                "127.0.0.1",
                "微信推广",
                "蓝海",
                "PRODUCT_5"
        );
        String content=sendRedPackage(sendRedPack);
        System.out.println(content);

    }
    @SuppressWarnings("unchecked")
	public static String sendRedPackage(SendRedPack sendRedPack)throws Exception{
    	 //将实体类转换为url形式
        String urlParamsByMap = Tool.getUrlParamsByMap(Tool.toMap(sendRedPack));
        //拼接我们再前期准备好的API密钥,前期准备第5条
        urlParamsByMap += "&key=支付key";
        //进行签名,需要说明的是,如果内容包含中文的话,要使用utf-8进行md5签名,不然会签名错误
        String sign = Tool.parseStrToMd5L32(urlParamsByMap).toUpperCase();
        sendRedPack.setSign(sign);
        //微信要求按照参数名ASCII字典序排序,这里巧用treeMap进行字典排序
        @SuppressWarnings("rawtypes")
		TreeMap treeMap = new TreeMap(Tool.toMap(sendRedPack));
        //然后转换成xml格式
        String soapRequestData = Tool.getSoapRequestData(treeMap);
        //发起请求前准备
        RequestBody body = RequestBody.create(MediaType.parse("text/xml;charset=UTF-8"), soapRequestData);
        Request request = new Request.Builder()
                .url("https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack")
                .post(body)
                .build();
        //为http请求设置证书
        SSLSocketFactory socketFactory = getSSL().getSocketFactory();
        X509TrustManager x509TrustManager = Platform.get().trustManager(socketFactory);
        OkHttpClient okHttpClient = new OkHttpClient.Builder().sslSocketFactory(socketFactory, x509TrustManager).build();
        //得到输出内容
        Response response = okHttpClient.newCall(request).execute();
        String content = response.body().string();
        return content;
    }
    public static SSLContext getSSL() throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyManagementException {
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        //证书位置自己定义
        FileInputStream instream = new FileInputStream(new File("D:/soft/apiclient_cert.p12"));
        try {
            keyStore.load(instream, "商户号".toCharArray());
        } finally {
            instream.close();
        }
        SSLContext sslcontext = SSLContexts.custom()
                .loadKeyMaterial(keyStore, "商户号".toCharArray())
                .build();
        return sslcontext;
    }

}

#效果
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

2016-12-27 12:33:10 zhanghao143lina 阅读数 1633
  • 微信公众号开发7-用户管理-微信开发php

    微信公众平台开发之微信用户开发管理是子恒老师《微信公众平台开发》视频教程的第7部。详细讲解了用php开发微信,对微信公众平台中的粉丝用户管理开发。内容包含微信公众平台用户分组,获取微信用户列表,查询用户详情等等。欢迎反馈,微信/QQ:68183131

    14030 人正在学习 去看看 秦子恒
  1. 微信发红包是比较流行的一件事情,老老少少都会使用微信发红包,接下来,小编给大家分享在生活中常碰到的一个php实现微信发红包程序的算法,希望这个程序对大家在做微信卡发的朋友有所帮助
  2.  使用PHP发红包,当我们输入红包数量和总金额后,PHP会根据这两个值进行随机分配每个金额,保证每个人都能领取到一个红包,每个红包金额不等,就是要求红包金额要有差异,所有红包金额总额应该等于总金额。
  3.  首先给大家分析下规律。
  4.  设定总金额为10元,有N个人随机领取:
  5. N=1 第一个
  6.  则红包金额=X元;
  7. N=2 第二个
  8.  为保证第二个红包可以正常发出,第一个红包金额=0.019.99之间的某个随机数。
  9.  第二个红包=10-第一个红包金额;
  10. N=3 第三个
  11.  红包1=0.019.99之间的某个随机数
  12.  红包2=0.01至(10-红包1-0.01)的某个随机数
  13.  红包3=10-红包1-红包2
  14.  ……
  15.  于是我们得到一个规律,在分配当前红包金额时,先预留剩余红白所需最少金额,然后在0.01至总金额-预留金额间取随机数,得到的随机数就是当前红包分配的金额。
  16.  实际应用中,程序先将红包金额分配好,即发红包时,红包个数以及每个红包的金额都分配好了,那么用户来抢红包时,我们随机给用户返回一个红包即可。
  17.  红包分配代码:
  18. $total=20;//红包总金额 
  19. $num=10;// 分成10个红包,支持10人随机领取 
  20. $min=0.01;//每个人最少能收到0.01元 
  21.  for ($i=1;$i<$num;$i++) 
  22.  { 
  23.  $safe_total=($total-($num-$i)*$min)/($num-$i);//随机安全上限 
  24.  $money=mt_rand($min*100,$safe_total*100)/100; 
  25.  $total=$total-$money; 
  26.  echo '第'.$i.'个红包:'.$money.' 元,余额:'.$total.' 元 '; 
  27.  }
  28. echo '第'.$num.'个红包:'.$total.' 元,余额:0 元';
  29.  
  30.  运行以上代码,会输出以下结果:
  31.  1个红包,金额2.08元,余额17.92
  32.  2个红包,金额1.81元,余额16.11
  33.  3个红包,金额0.15元,余额15.96
  34.  4个红包,金额1.61元,余额14.35
  35.  5个红包,金额1.11元,余额13.24
  36.  6个红包,金额1.51元,余额11.73
  37.  7个红包,金额1.21元,余额10.52
  38.  8个红包,金额2.58元,余额7.94
  39.  9个红包,金额5.4元,余额2.54
  40.  10个红包,金额2.54元,余额0
  41.  以上就是使用php实现微信发红包程序,希望对大家有所帮助
2016-09-06 09:39:30 wei389083222 阅读数 6523
  • 微信公众号开发7-用户管理-微信开发php

    微信公众平台开发之微信用户开发管理是子恒老师《微信公众平台开发》视频教程的第7部。详细讲解了用php开发微信,对微信公众平台中的粉丝用户管理开发。内容包含微信公众平台用户分组,获取微信用户列表,查询用户详情等等。欢迎反馈,微信/QQ:68183131

    14030 人正在学习 去看看 秦子恒

前几天用java实现了微信商户发红包给用户的功能。现在整理一下,方便以后参考。

总体的步骤是:
- 1.在微信客户端转发自己的uri路径
- 2.拿到微信授权的Code
- 3.获取用户openID
- 4.获取用户信息
- 5.给指定用户发送红包

在开发之前了解一下完整的微信需要准备的参数:
微信公众账号:appid
商户号:mchid
发送红包方的名称(商户名称):sendname
红包祝福语:wishing
ip地址:ip
秘钥:key
安全码:secret
证书:apiclient_cert.p12
在接下来步骤中会详细介绍每参数需要注意的地方。

在微信客户端转发自己的uri路径

在查看微信的官方文档有具体说明:用户在微信客户端访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑。
但是微信授权回调的域名可以参考微信官方文档关于域名的要求:
https://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html
这里要注意的是你的后台服务必须是要发布在80端口下,列如tomcat发布在80端口下。

域名配置和服务都发布好之后,就可以调用微信的接口获取Code啦。
url:
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
params介绍:
appid:发送方的微信公众账号,微信分配的公众账号ID(企业号corpid即为此appId)。接口传入的所有appid应该为公众号的appid(在mp.weixin.qq.com申请的),不能为APP的appid(在open.weixin.qq.com申请的)。eg:wx8888888888888888
redirect_uri:转发到自己的url,eg:www.abc.com/项目名/weixin/hongbao
scope:Scope为snsapi_base,,Scope为snsapi_userinfo时弹出微信获取用户信息授权提示页面。
state:重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节
注意:必须带上#wechat_redirect,并且参数顺序必须和上述一致。

如果你是通过你的服务器发起的请求代码如下:

 your control:
 String    url=https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx8888888&redirect_uri=http://www.abc.com/test/weixin/hongbao&response_type=code&scope=snsapi_base&state=1#wechat_redirect
 //微信授权转发
 response.sendRedirect(url);

如果直接在微信客户端打开上述url,只要确保REDIRECT_URI参数正确,就可以直接转发到你的地址里面去。

遇到的问题:

  1. 请在微信客户端打开连接,可以将链接生成二维码扫描。
  2. 确保转发url已审批,并在80端口下发布,不然会微信会返回提示:REDIRECT_URI参数错误。

拿到微信授权的Code

微信在转发到指定的url后会携带两个参数:code和state
code:是用来获取用户的openId接口必要的参数之一。
state:是你在转发url时填写的参数。是一个双方之间的凭证,可用来判断是否合法。

www.abc.com/test/weixin/hongbao的代码:

@RequestMapping(value="/hongbao",method={RequestMethod.GET,RequestMethod.POST})
public String hongbao( @RequestParam(value = "code", required = false) String code,@RequestParam(value = "state", required = false) String state,HttpServletResponse response,HttpServletRequest request){  
  request.getSession();
  //这样就可以获取code和state啦,打印一下
  Log.logger.info(request.getParameter("code"));                       Log.logger.info(request.getParameter("state"));

  //获取openiD
  ...
 }

获取用户openID
调用微信接口获取openID:
url: https://api.weixin.qq.com/sns/oauth2/access_token
method: GET
params: appid:wx88888 , secret:qwe123123, code:上面获取的code, grant_type:一个定值authorization_code

代码:

SortedMap<String, String> params = new TreeMap<String, String>();  
        params.put("appid",wechatConfig.getWxappid());  
        params.put("secret",wechatConfig.getSecret());  
        params.put("code",code);
        params.put("grant_type","authorization_code");
        //将参数进行排序
        String sortparams = WeChatUtils.getSortParams(params);
        //调用接口获取结果
        String result=HttpNetUtils.getInstance().sendGet(WeChatContants.WECHAT_OPENID_URL,sortparams);
        //解析result获取opeId;
         WeiChatGetOpenIdVo getOpenIdVo = JSON.parseObject(result, WeiChatGetOpenIdVo.class);   
        String openId=getOpenIdVo.getOpenid();

返回结果参数有:
{
“access_token”:”ACCESS_TOKEN”,
“expires_in”:7200,
“refresh_token”:”REFRESH_TOKEN”,
“openid”:”OPENID”,
“scope”:”SCOPE”,
“unionid”: “o6_bmasdasdsad6_2sgVt7hMZOPfL”
}
咱们只要获取他的openID即可,其他的参数说明可以参考官方文档 。

获取用户信息
获取用户信息前还需要一个必要的参数:该公众账号下的access_token,每个公众账号的token每天是有次数限制的(2000次)其他的限制可参考:
这里写图片描述

调用接口获取token:
url:https://api.weixin.qq.com/cgi-bin/token
method:GET
params:grant_type:”client_credential “, appid:wx88888 , secret:qwe1232123

代码:

SortedMap<String, String> params = new TreeMap<String, String>();  
                params.put("grant_type","client_credential");  
                params.put("appid",wechatConfig.getWxappid());  
                params.put("secret",wechatConfig.getSecret());  

                String sortparams = WeChatUtils.getSortParams(params);
                String result = HttpNetUtils.getInstance().sendGet(WeChatContants.WECHAT_ACCESSTOKEN_URL,sortparams);
                WeiChatAccessTokenVo token = JSON.parseObject(result, WeiChatAccessTokenVo.class);
               String accessToken=token.getAccess_token(); 
    //可以将token存在数据库里,不必每次都去调用微信接口获取。判断超过7200s后才去更新。

文档位置:获取接口调用凭证–>获取access_token
这里写图片描述

拿到token和openID后调用获取用户信息接口:
url: https://api.weixin.qq.com/cgi-bin/user/info
method: GET
params: access_token:上面获取的,openid:上面获取的,lang:”zh_CN”

代码:

SortedMap<String, String> params = new TreeMap<String, String>();  
        params.put("access_token",accessToken);  
        params.put("openid",openId);  
        params.put("lang","zh_CN");
        String sortparams = WeChatUtils.getSortParams(params);      
        String result=HttpNetUtils.getInstance().sendGet(WeChatContants.WECHAT_USERINFO_URL,sortparams);        
    JSON.parseObject(result.trim(),WeiChatUserInfoVo.class);

返回结果:
这里写图片描述

注意:如果该用户没有关注该微信公众账号是不能获取这么详细的信息的,没有关注的返回:subscribe:0,和openid。
未关注的情况下获取详细信息:
你也可以采用在转发url时提示用户授权获取他的详细信息,即scope=snsapi_userinfo

给指定用户发送红包
其实拿到用户的openID后就可已给指定的用户发红包啦。
url: https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack
证书:需要证书
method: POST
params: nonce_str 随机字符串,
mch_billno 商户订单号(商户号+一个10位不能重复的字符)
mch_id 商户号
wxappid 微信公众账号
send_name 发送方名字
re_openid 接受红包者的openID
total_amount 发送总金额
total_num 发送总数量
wishing 红包祝福语
client_ip 调用接口的机器Ip地址
act_name 活动名字
remark 备注
sign 将以上参数配上key打包签名的值
参数详细说明:
https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=13_4&index=3
关于证书和签名可查看:
https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=4_3
这里写图片描述

代码:

//使用该企业下的微信红包参数来封装调取微信接口的参数
            SortedMap<String, String> params = new TreeMap<String, String>();  
               params.put("wxappid",wechatConfig.getWxappid());  
               params.put("nonce_str",WeChatUtils.createNonceStr());  
               params.put("mch_billno",WeChatUtils.createBillNo(wechatConfig.getMchId()));  
               params.put("mch_id",wechatConfig.getMchId());  
               params.put("send_name",wechatConfig.getSendName());  
               params.put("re_openid",openId);  
               params.put("total_amount",100+"");//分 
               params.put("total_num", 1+"");  
               params.put("wishing", wechatConfig.getWishing());  
               params.put("client_ip", wechatConfig.getHostIp());  
               params.put("act_name", wechatConfig.getActName());  
               params.put("remark",wechatConfig.getRemark());
            //将参数转换为有序的
            String sortParams = WeChatUtils.getSortParams(params);
            //将参数打包签名
            String key=wechatConfig.getMyKey();
            WeChatUtils.sign(params,sortParams,key);
            //将参数已xml的装换为格式
            String paramsXml = WeChatUtils.getRequestXml(params);
            //调取微信发红包接口
            String contextPath=request.getSession().getServletContext().getRealPath("");
            //httppost请求鞋带证书
            String certPath=contextPath.substring(0,contextPath.lastIndexOf("\\"))+wechatConfig.getCert();
            String certPassword=wechatConfig.getMchId();
            String rets =HttpNetUtils.getInstance().certPost(WeChatContants.WECHAT_SENDHONGBAO_URL,paramsXml,certPath,certPassword);  
            Log.logger.info(rets);

打包签名和获取随机字符的代码也贴出来方便参考:

 /**
         * 生成随机字符串
         * @return
         */
        public static String createNonceStr() {
            return UUID.randomUUID().toString().toUpperCase().replace("-", "");
        }
/**
         * 生成商户订单号
         * @param mch_id  商户号
         * @return
         */
        public static String createBillNo(String mchId){
            //组成: mch_id+yyyymmdd+10位一天内不能重复的数字
            //10位一天内不能重复的数字实现方法如下:
            Date dt=new Date();
            SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
            String nowTime= df.format(dt);
            return mchId + nowTime + getRandomNum(4);
        }   
/**
         * 生成特定位数的随机数字
         * @param length
         * @return
         */
        public static String getRandomNum(int length) {
            String val = "";
            Random random = new Random();
            for (int i = 0; i < length; i++) {
                val += String.valueOf(random.nextInt(10));
            }
            return val;
        }   
/**
     * 将传入的参数加上key并通过MD5签名
     * 
     * @param params
     */
    public static void sign(SortedMap<String, String> params,String sortparams,String key){
        //添加秘钥并且签名  
        sortparams +="key="+key;
        String sign = null;
        try {
            sign = MD5Util.MD5(sortparams);
        } catch (Exception e) {
            e.printStackTrace();
        }
        params.put("sign", sign);
    }
     /**
          * 将SortedMap集合里的按顺序参数转换为:name=value&name1=value1
          * 按顺序
          * @param params
          */
         public static String getSortParams(SortedMap<String, String> params){
                Set<Entry<String,String>> entrys=params.entrySet();  
                Iterator<Entry<String,String>> it=entrys.iterator();  
                String result = "";
                while(it.hasNext())  
                {  
                   Entry<String,String> entry=it.next();  
                   result +=entry.getKey()+"="+entry.getValue()+"&";
                }  
                return result;
            }       
/**
     * 将传入的参数装换为xml格式,用于调取微信红包接口
     * @param params
     * @return
     */
     public static String getRequestXml(SortedMap<String,String> params){
            StringBuffer sb = new StringBuffer();
            sb.append("<xml>");
            Set es = params.entrySet();
            Iterator it = es.iterator();
            while(it.hasNext()) {
                Map.Entry entry = (Map.Entry)it.next();
                String k = (String)entry.getKey();
                String v = (String)entry.getValue();
                if ("nick_name".equalsIgnoreCase(k)||"send_name".equalsIgnoreCase(k)||"wishing".equalsIgnoreCase(k)||"act_name".equalsIgnoreCase(k)||"remark".equalsIgnoreCase(k)||"sign".equalsIgnoreCase(k)) {
                    sb.append("<"+k+">"+"<![CDATA["+v+"]]></"+k+">");
                }else {
                    sb.append("<"+k+">"+v+"</"+k+">");
                }
            }
            sb.append("</xml>");
            return sb.toString();
     }      

HTTP发送POST方法的携带证书请求代码:

 /**
        * 向指定URL 发送POST方法的携带证书请求
        * 
        * @param url
        * @param requestXML   xml格式的参数         
        * @param certPath     证书的地址
        * @param certPassword 证书的密码
        *           
        * @return 远程资源的响应结果
        */
       @SuppressWarnings("deprecation")
       public String certPost(String url,String requestXML,String cretPath,String certPwd) throws NoSuchAlgorithmException, CertificateException, IOException, KeyManagementException, UnrecoverableKeyException, KeyStoreException{
            KeyStore keyStore  = KeyStore.getInstance("PKCS12");

            FileInputStream instream = new FileInputStream(new File(cretPath));
            keyStore.load(instream, certPwd.toCharArray());
            instream.close();

            SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, certPwd.toCharArray()).build();
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                    sslcontext,
                    new String[] { "TLSv1" },
                    null,
                    SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
            CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
            String result = "";
            try {

                HttpPost httpPost = new HttpPost(url);
                StringEntity  reqEntity  = new StringEntity(requestXML,"UTF-8");
                // 设置类型 
                reqEntity.setContentType("application/x-www-form-urlencoded"); 
                httpPost.setEntity(reqEntity);
                CloseableHttpResponse response = httpclient.execute(httpPost);
                try {
                    HttpEntity entity = response.getEntity();
                    if (entity != null) {
                        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent(),"UTF-8"));
                        String text;
                        while ((text = bufferedReader.readLine()) != null) {
                            result +=text;
                        }
                    }
                    EntityUtils.consume(entity);
                } finally {
                    response.close();
                }
            } finally {
                httpclient.close();
            }
            return result;
        } 

如果成功调通接微信会返回:xml格式的数据。例如:

这里写图片描述

如果用户没有关注该公众账号:发送红包时一个微信客户端的服务通知。
如果用户关注了公众账号:发送红包是在该公众账号下的一条消息。

到这里微信商户给用户发送红包和获取用户信息的流程就全部完成啦。
如果你看了之后对你哪怕是有一丢丢的帮助,别忘了给我点赞啊。

最后附上微信官方文档的地址:

  1. 用户授权及获取用户信息:https://mp.weixin.qq.com/wiki/14/bb5031008f1494a59c6f71fa0f319c66.html
  2. 微信支付和发红包:https://pay.weixin.qq.com/wiki/doc/api/index.html
2016-12-27 16:31:38 getluo 阅读数 1358
  • 微信公众号开发7-用户管理-微信开发php

    微信公众平台开发之微信用户开发管理是子恒老师《微信公众平台开发》视频教程的第7部。详细讲解了用php开发微信,对微信公众平台中的粉丝用户管理开发。内容包含微信公众平台用户分组,获取微信用户列表,查询用户详情等等。欢迎反馈,微信/QQ:68183131

    14030 人正在学习 去看看 秦子恒

首先需要公众号有模板消息的权限,这里不多说明了。


要实现的功能就是在第三方网站通过调用接口在对给用户发消息,当然也可以给用户标签组群发消息。


获取access_token代码:

$weixin_appid = "";
$weixin_secret = "";
$weixin_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=".$weixin_appid."&secret=".$weixin_secret;
$weixin_return = get_url_contents ( $weixin_url );
$weixin = json_decode ( $weixin_return );
$access_token = $weixin->access_token;
//模板ID
$template_id = "";

发送消息代码:

				$dataarray = array(
						"touser"=>$openid,//用户的openid
						"template_id"=>$template_id,//消息模板id
						"url"=>$_POST['yanbao_url'],
						"data"=>array(
						    "first"=>array(
				                       "value"=>$_POST['yanbao_title'],
				                       "color"=>"#173177"
				                    ),
				                    "keyword1"=>array(
				                       "value"=>$_POST['yanbao_name'],
				                       "color"=>"#173177"
				                    ),
				                    "keyword2"=>array(
				                       "value"=>$_POST['yanbao_desc'],
				                       "color"=>"#173177"
				                    ),
				                    "remark"=>array(
				                       "value"=>$_POST['yanbao_more'],
				                       "color"=>"#173177"
				                    )
								)
				            );
				$data = json_encode($dataarray);
				$output = json_decode(do_post($url,$data));//do_post是模拟post的函数,请自行定义
				if($output->errcode == '0'){
					echo '<div class="updated"><p>发送成功!</p></div>';
				}else{
					echo '<div class="error"><p>发送失败!错误码:'.$output->errcode.'</p></div>';
				}

获取用户标签代码:

				<tr valign="top">
					<th scope="row"><label>用户标签</label></th>
					<td>
						<select name="yanbao_user_tag" id="yanbao_user_tag" style="min-width: 150px">
							<option value="">请选择</option>
							<option value="all">所有用户</option>
							<?php 
								$yanbao_user_tag_url = "https://api.weixin.qq.com/cgi-bin/tags/get?access_token=".$access_token;
								$yanbao_user_tag_return = get_url_contents ( $yanbao_user_tag_url );
								$yanbao_user_tag = json_decode ( $yanbao_user_tag_return );
								$tags = $yanbao_user_tag->tags;
								foreach ($tags as $tag) {
									echo '<option value="'.$tag->id.'">'.$tag->name.'('.$tag->count.')</option>';
								}
							?>		
						</select>
						<span class="description">(选择了用户标签就是<strong>群发</strong>给该标签下的用户)</span>
					</td>
				</tr>

获取用户代码:

<tr valign="top">
					<th scope="row"><label>用户</label></th>
					<td>
						<select name="yanbao_user" id="yanbao_user">
							<option selected="selected" value="">请选择</option>
							<?php 
								/*
								$yanbao_user_url = "https://api.weixin.qq.com/cgi-bin/user/get?access_token=".$access_token."&next_openid=";
								$yanbao_user_return = get_url_contents ( $yanbao_user_url );
								$yanbao_user = json_decode ( $yanbao_user_return );
								$openids = $yanbao_user->data->openid;
								foreach ($openids as $openid) {
									$added_user = Moge_check_weixin_user_added($openid);
									if($added_user){
										echo '<option value="'.$openid.'">'.$added_user->nickname.'</option>';
									}else{
										$user = Moge_weixin_get_user_info($access_token,$openid);
										$wpdb->query("insert into ".$wpdb->prefix."erphp_weixin_users (openid,nickname) values('".$openid."','".$user->nickname."')");
										echo '<option value="'.$openid.'">'.$user->nickname.'</option>';
									}
								}*/
								
								$weixin_users = Moge_get_weixin_users();//获取本地数据库里的微信用户函数
								if($weixin_users){
									foreach ($weixin_users as $user) {
										echo '<option value="'.$user->openid.'">'.$user->nickname.'</option>';
									}
								}
								
							?>
						</select>
						<span class="description">(若已选择了用户标签,此项选择就无效了。)</span>
					</td>
				</tr>


这里主要遇到一个问题就是如果高效快速的获取用户列表的昵称(nickname),通过以下的接口

https://api.weixin.qq.com/cgi-bin/user/get?access_token=ACCESS_TOKEN&next_openid=NEXT_OPENID

只能获取用户的openid,我尝试过针对每个用户获取其详细资料,结果页面直接卡死了(用户数量太多)。虽然看到有批量获取资料,但是不适合用,最后没办法,只能先循环获取然后存在本地数据库,定期获取更新下数据库了,有个弊端就是取消关注的人还是会在本地数据库里。

2016-10-27 10:19:11 u012210379 阅读数 3110
  • 微信公众号开发7-用户管理-微信开发php

    微信公众平台开发之微信用户开发管理是子恒老师《微信公众平台开发》视频教程的第7部。详细讲解了用php开发微信,对微信公众平台中的粉丝用户管理开发。内容包含微信公众平台用户分组,获取微信用户列表,查询用户详情等等。欢迎反馈,微信/QQ:68183131

    14030 人正在学习 去看看 秦子恒
  1. 给用户发红包
  2. 用户买了商品,剩下的钱,可以自己再提出来。

问题1我纠结了好久,并没有找到合适解决方法,因为按照这个要求,操作由后台发起,由后台执行,而这样做,微信是不允许的这也是为什么我会写一篇关于支付宝提现的帖子,但你去看支付宝的文档,会发现过程极其繁琐,还有时间差,而且微信与支付宝打架,这个功能还得放在微信外的页面,用户体验非常不好。

经过与老板的讨价还价,终于改变为了问题2 : 将发起者改为用户,这样以上的问题,就都不是问题了。

阅读代码之前墙裂建议先看完官方文档

业务场景:
用户点击页面上的按钮,post请求发送至后台,获取用户的id和要提现的金额,经过校验向用户微信钱包打款。

func (c *WXRedPacketController) Post() {

    uid, _ := c.GetSession("uid").(string)
    packegtMoney, err := c.GetFloat("packegtMoney", 0)

    o := orm.NewOrm()

    if err == nil {

        //检查用户钱包内是否有这么多钱可供提现
        uwallet := models.UserWallet{Uid:uid}
        if _, _, err := o.ReadOrCreate(&uwallet, "uid"); err == nil {

            umoney, _ := strconv.ParseFloat(uwallet.Money, 64)
            if umoney >= packegtMoney {

                //提现至微信红包
                str_req := makeRedPacketXML(packegtMoney, uid)
                if ok, errInfo := sendRedPacketRequest(str_req, uid); ok {

                    uwallet.Money = strconv.FormatFloat(math.Floor((umoney - packegtMoney) * 100) / 100, 'f', 2, 64)
                    _, err := o.Update(&uwallet, "money")
                    if err != nil {
                        fmt.Println("用户提现更新钱包失败", err)
                        logUtils.GetLog().Error("用户提现更新钱包失败", err)
                        c.Data["errinfo"] = "更新钱包失败"
                    } else {
                        //插入 wallet-his
                        _, err = o.Insert(&models.UserWalletHis{Uid:uid, MoneyChange:strconv.FormatFloat(math.Floor(packegtMoney * 100) / 100, 'f', 2, 64), BonusChange:"0", ChangeReason:models.DRAW, BankType:"", BankNum:"", ReasonOrder:"", ReasonPerson:"", CreateTime:time.Now().Format(TIMELAYOUT), })
                        if err != nil {
                            fmt.Printf("插入 UserWalletHis 错误 %v", err)
                            logUtils.GetLog().Error("插入 UserWalletHis 错误 %v", err)
                        }
                    }

                    c.Redirect("/wx_red_packet", 302)
                } else {
                    c.Data["errinfo"] = errInfo
                }

            } else {
                c.Data["errinfo"] = "钱包余额不足"
            }
        } else {
            c.Data["errinfo"] = "钱包查询错误"
        }
    }

    c.Data["money"] = showWalletMoney(o, uid)//可提现
    c.TplName = "wx_red_packget.html"
}

//生成红包的申请xml money 提现的金额 元
func makeRedPacketXML(money float64, uid string) string {

    //拼接xml
    var redPacket models.RedPacket
    redPacket.Nonce_str = RandomStrUtil.GetRandomString(32)
    redPacket.Mch_billno = beego.AppConfig.String("shopKey") + time.Now().Format(TIMELAYOUT3) + strconv.FormatInt(time.Now().Unix(), 10)
    redPacket.Mch_id = beego.AppConfig.String("shopKey")
    redPacket.Appid = beego.AppConfig.String("APPID")
    redPacket.Send_name = "商城"
    redPacket.Re_openid = uid
    redPacket.Total_amount = int(money * 100)//分钱
    redPacket.Total_num = 1
    redPacket.Wishing = "收红包啦"
    redPacket.Client_ip = "122.122.122.122"
    redPacket.Act_name = "红包名称"
    redPacket.Remark = "红包说明"

    var n map[string]interface{}
    n = make(map[string]interface{}, 0)
    n["nonce_str"] = redPacket.Nonce_str
    n["mch_billno"] = redPacket.Mch_billno
    n["mch_id"] = redPacket.Mch_id
    n["wxappid"] = redPacket.Appid
    n["send_name"] = redPacket.Send_name
    n["re_openid"] = redPacket.Re_openid
    n["total_amount"] = redPacket.Total_amount
    n["total_num"] = redPacket.Total_num
    n["wishing"] = redPacket.Wishing
    n["client_ip"] = redPacket.Client_ip
    n["act_name"] = redPacket.Act_name
    n["remark"] = redPacket.Remark

    redPacket.Sign = wxpayCalcSign(n, PAY_API_KEY) //这个key 微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全

    bytes_req, err := xml.Marshal(redPacket)
    if err != nil {
        fmt.Println("转换为xml错误:", err)
        logUtils.GetLog().Error("转换为xml错误:", err)
    }

    str_req := strings.Replace(string(bytes_req), "RedPacket", "xml", -1)
    fmt.Println("发红包参数转换为xml--------", str_req)
    return str_req
}

func sendRedPacketRequest(str_req, uid string) (bool, string) {

    bytes_req := []byte(str_req)

    //发送unified order请求.
    req, err := http.NewRequest("POST", "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack", bytes.NewReader(bytes_req))
    if err != nil {
        fmt.Println("发送红包 Request发生错误,原因:", err)
        logUtils.GetLog().Error("发送红包 Request发生错误,原因:", err)
        return false, "未知错误"

    }
    req.Header.Set("Accept", "application/xml")
    //这里的http header的设置是必须设置的.
    req.Header.Set("Content-Type", "application/xml;charset=utf-8")

    tlsConfig, err := WxPlatUtil.GetTLSConfig()
    if err != nil {
        fmt.Println("解析证书出现错误", err)
        return false, "未知错误"
    }

    tr := &http.Transport{TLSClientConfig: tlsConfig}
    client := &http.Client{Transport: tr}

    resp, _err := client.Do(req)
    if _err != nil {
        fmt.Println("请求发送红包接口发送错误, 原因:", _err)
        logUtils.GetLog().Error("请求发送红包接口发送错误, 原因:", _err)
        return false, "未知错误"
    }

    respBytes, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Println("请求发送红包解析返回body错误", err)
        logUtils.GetLog().Error("请求发送红包解析返回body错误", err)
        return false, "未知错误"
    } else {
        fmt.Println("请求发送红包解析返回===================>", string(respBytes))
        redPacketRes := models.RedPacketRes{}
        if err := xml.Unmarshal(respBytes, &redPacketRes); err == nil {

            recodeRedPacketRes(&redPacketRes, uid)//记录存入数据库

            if redPacketRes.Return_code == "SUCCESS" {

                if redPacketRes.Result_code == "SUCCESS" {

                    return true, "红包领取成功"
                } else {
                    return false, redPacketRes.Err_code_des
                }

            } else {
                //签名失败
                return false, "微信签名失败"
            }

        } else {
            fmt.Println("红包领取记录解析错误", err)
            logUtils.GetLog().Error("红包领取记录解析错误", err)
        }
    }

    return false, "未知错误"
}

func wxpayCalcSign(mReq map[string]interface{}, key string) string {

    //fmt.Println("========STEP 1, 对key进行升序排序.========")
    //fmt.Println("微信支付签名计算, API KEY:", key)
    //STEP 1, 对key进行升序排序.
    sorted_keys := make([]string, 0)
    for k, _ := range mReq {
        sorted_keys = append(sorted_keys, k)
    }

    sort.Strings(sorted_keys)

    //fmt.Println("========STEP2, 对key=value的键值对用&连接起来,略过空值========")
    //STEP2, 对key=value的键值对用&连接起来,略过空值
    var signStrings string
    for _, k := range sorted_keys {
        //fmt.Printf("k=%v, v=%v\n", k, mReq[k])
        value := fmt.Sprintf("%v", mReq[k])
        if value != "" {
            signStrings = signStrings + k + "=" + value + "&"
        }
    }

    //fmt.Println("========STEP3, 在键值对的最后加上key=API_KEY========")
    //STEP3, 在键值对的最后加上key=API_KEY
    if key != "" {
        signStrings = signStrings + "key=" + key
    }

    //fmt.Println("========STEP4, 进行MD5签名并且将所有字符转为大写.========")
    //STEP4, 进行MD5签名并且将所有字符转为大写.
    md5Ctx := md5.New()
    md5Ctx.Write([]byte(signStrings))
    cipherStr := md5Ctx.Sum(nil)
    upperSign := strings.ToUpper(hex.EncodeToString(cipherStr))

    return upperSign
}

func recodeRedPacketRes(redPacketRes *models.RedPacketRes, uid string) {

    //插入数据库 红包领取记录
    o := orm.NewOrm()
    wxUser := models.WxUser{WxId:uid}
    if err := o.Read(&wxUser, "wx_id"); err == nil {
        redPacketHis := models.RedPacketResHis{RedPacketRes:*redPacketRes, Name:wxUser.Name, Phone:wxUser.Phone, CreateTime:time.Now().Format(TIMELAYOUT)}
        _, err := o.Insert(&redPacketHis)
        if err != nil {
            fmt.Println("插入红包领取记录错误", err)
            logUtils.GetLog().Error("红包领取记录", err)
        }
    } else {
        fmt.Println("查询用户信息错误", err)
        logUtils.GetLog().Error("查询用户信息错误", err)
    }
}

这就OK啦,用户的微信钱包是实时更新的。当然要是想再友好点,可以给用户发个消息,下一篇,就来写微信的客服功能。

微信企业红包发放

博文 来自: wangchaoxiaoban
没有更多推荐了,返回首页