休息食客

随心而动

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  16 随笔 :: 7 文章 :: 2 评论 :: 0 Trackbacks
开发之前,最好有一些微信开发的经验,先看一下文档,了解一下https://pay.weixin.qq.com/wiki/doc/api/cash_coupon.php?chapter=13_5
文档过了一遍之后,脑海里应该有些印象了,废话不说,进入开发。
准备参数,一个一个来。
参数1:随机字符串    nonce_str
//获取随机数,这里设定长度20,只要不成长于32位
String nonce_str = RandomStringGenerator.getRandomStringByLength(20); 

写一个RandomStringGenerator类,写一个getRandomStringByLength静态方法
import java.util.Random;
/**
 * User: rizenguo
 * Date: 2014/10/29
 */
public class RandomStringGenerator {
    /**
     * 获取一定长度的随机字符串
     * @param length 指定字符串长度
     * @return 一定长度的字符串
     */
    public static String getRandomStringByLength(int length) {
        String base = "abcdefghijklmnopqrstuvwxyz0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < length; i++) {
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }
        return sb.toString();
    }
}
参数2 : 商户号   mch_id
String mch_id = “xxxxxxxx”;

参数3 : 商户订单号  mch_billno

String mch_billno = mch_id + GenerateSequenceUtil.generateSequenceNo();

些一个GenerateSequenceUtil类,写一个generateSequenceNo方法

import java.text.DecimalFormat;
import java.text.FieldPosition;
import java.text.Format;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
 
import org.apache.log4j.Logger;
 
public class GenerateSequenceUtil {
 
 /** .log */
 private static final Logger logger = Logger.getLogger(GenerateSequenceUtil.class);
 
 /** The FieldPosition. */
 private static final FieldPosition HELPER_POSITION = new FieldPosition(0);
 
 /** This Format for format the data to special format. */
 private final static Format dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
 
 /** This Format for format the number to special format. */
 private final static NumberFormat numberFormat = new DecimalFormat("0000");
 
 /** This int is the sequence number ,the default value is 0. */
 private static int seq = 0;
 
 private static final int MAX = 9999;
 
 /**
 * 时间格式生成序列
 * @return String
 */
 public static synchronized String generateSequenceNo() {
 
  Calendar rightNow = Calendar.getInstance();
   
  StringBuffer sb = new StringBuffer();
   
  dateFormat.format(rightNow.getTime(), sb, HELPER_POSITION);
   
  numberFormat.format(seq, sb, HELPER_POSITION);
   
  if (seq == MAX) {
   seq = 0;
  } else {
   seq++;
  }
   
  logger.info("THE SQUENCE IS :" + sb.toString());
   
  return sb.toString();
 }

参数4: 公众账号appid   wxappid

这个参数可以在微信开发者中心里面看到
//微信分配的公众账号ID
String wxappid = "wx8888888888888888";
参数5 : 商户名称   send_name
随便写
String send_name = "测试者";   
参数6 : 用户openid   re_openid
微信号和公众号对应的唯一的加密过的字符串
String re_openid = "xxxxxxxxxxxxxxxxxxxxxxx";
参数7: 付款金额   total_amount
int total_amount = 100;    单位分
参数8 : 红包发放总人数  total_num
int total_num = 1;
参数9 : 红包祝福语   wishing
随便写
String wishing = "测试";
参数10 : Ip地址   client_ip
String client_ip = InetAddress.getLocalHost().getHostAddress().toString();
这里可能报异常,需要抓取一下
参数11 : 活动名称   act_name
随便写
String act_name = "测试创建20150906";
参数12 : 备注  remark
随便写
String remark = "测试";
最后一个参数sign
//签名
  Map<String,Object> map = new HashMap<String,Object>();
  map.put("nonce_str", nonce_str);
  map.put("mch_billno", mch_billno);
  map.put("mch_id", mch_id);
  map.put("wxappid", wxappid);
  map.put("send_name", send_name);
  map.put("re_openid", re_openid);
  map.put("total_amount", total_amount);
  map.put("total_num", total_num);
  map.put("wishing", wishing);
  map.put("client_ip", client_ip);
  map.put("act_name", act_name);
  map.put("remark", remark);
  String sign = Signature.getSign(map);
写一个Signature类,代码如下:
import org.xml.sax.SAXException;
import com.yxht.core.common.tools.LoadProperties;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
/**
 * User: rizenguo
 * Date: 2014/10/29
 * Time: 15:23
 */
public class Signature {
    /**
     * 签名算法
     * @param o 要参与签名的数据对象
     * @return 签名
     * @throws IllegalAccessException
     */
    public static String getSign(Object o) throws IllegalAccessException {
        ArrayList<String> list = new ArrayList<String>();
        Class cls = o.getClass();
        Field[] fields = cls.getDeclaredFields();
        for (Field f : fields) {
            f.setAccessible(true);
            if (f.get(o) != null && f.get(o) != "") {
                list.add(f.getName() + "=" + f.get(o) + "&");
            }
        }
        int size = list.size();
        String [] arrayToSort = list.toArray(new String[size]);
        Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < size; i ++) {
            sb.append(arrayToSort[i]);
        }
        String result = sb.toString();
        result += "key=" + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";   //这里是开发者中心里面服务器配置里面的消息加解密密钥
        Util.log("Sign Before MD5:" + result);
        result = MD5.MD5Encode(result).toUpperCase();
        Util.log("Sign Result:" + result);
        return result;
    }
    public static String getSign(Map<String,Object> map){
        ArrayList<String> list = new ArrayList<String>();
        for(Map.Entry<String,Object> entry:map.entrySet()){
            if(entry.getValue()!=""){
                list.add(entry.getKey() + "=" + entry.getValue() + "&");
            }
        }
        int size = list.size();
        String [] arrayToSort = list.toArray(new String[size]);
        Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < size; i ++) {
            sb.append(arrayToSort[i]);
        }
        String result = sb.toString();
        result += "key=" + LoadProperties.getSystemdefaultValue("APP_KEY");
        result = MD5.MD5Encode(result).toUpperCase();
        return result;
    }
    /**
     * 从API返回的XML数据里面重新计算一次签名
     * @param responseString API返回的XML数据
     * @return 新鲜出炉的签名
     * @throws ParserConfigurationException
     * @throws IOException
     * @throws SAXException
     */
    public static String getSignFromResponseString(String responseString) throws IOException, SAXException, ParserConfigurationException {
        Map<String,Object> map = XMLParser.getMapFromXML(responseString);
        //清掉返回数据对象里面的Sign数据(不能把这个数据也加进去进行签名),然后用签名算法进行签名
        map.put("sign","");
        //将API返回的数据根据用签名算法进行计算新的签名,用来跟API返回的签名进行比较
        return Signature.getSign(map);
    }
    /**
     * 检验API返回的数据里面的签名是否合法,避免数据在传输的过程中被第三方篡改
     * @param responseString API返回的XML数据字符串
     * @return API签名是否合法
     * @throws ParserConfigurationException
     * @throws IOException
     * @throws SAXException
     */
    public static boolean checkIsSignValidFromResponseString(String responseString) throws ParserConfigurationException, IOException, SAXException {
        Map<String,Object> map = XMLParser.getMapFromXML(responseString);
        Util.log(map.toString());
        String signFromAPIResponse = map.get("sign").toString();
        if(signFromAPIResponse=="" || signFromAPIResponse == null){
            Util.log("API返回的数据签名数据不存在,有可能被第三方篡改!!!");
            return false;
        }
        Util.log("服务器回包里面的签名是:" + signFromAPIResponse);
        //清掉返回数据对象里面的Sign数据(不能把这个数据也加进去进行签名),然后用签名算法进行签名
        map.put("sign","");
        //将API返回的数据根据用签名算法进行计算新的签名,用来跟API返回的签名进行比较
        String signForAPIResponse = Signature.getSign(map);
        if(!signForAPIResponse.equals(signFromAPIResponse)){
            //签名验不过,表示这个API返回的数据有可能已经被篡改了
            Util.log("API返回的数据签名验证不通过,有可能被第三方篡改!!!");
            return false;
        }
        Util.log("恭喜,API返回的数据签名验证通过!!!");
        return true;
    }
}
先讲到这里,所有参数都准备好了,下一步是组成xml
声明:工具类代码参考微信支付开发的demo,有些自己做了小改动。
posted on 2015-09-10 15:35 休息食客 阅读(859) 评论(0)  编辑  收藏 所属分类: java

只有注册用户登录后才能发表评论。


网站导航: