微信小程序> 微信小程序支付详解:

微信小程序支付详解:

浏览量:699 时间: 来源:qq_41971087

前段时间在公司的开发了一个微信小程序的项目,今天来说一说微信小程序的支付,有很多优秀的文章都说了小程序支付的
开发流程步骤,这里我们推荐一个博主以前就是看他的开发小程序支付:https://github.com/1913045515/weixin
现在来说说我对小程序支付的理解,首先我们在开发文档中找到小程序支付流程,

小程序
上面的图是我们支付的流程图,下面是开发要调用接口的顺序,
首先第一步我们获取openid,因为在统一下单接口中的交易类型是JSAPI,是必须要填openid
第二步就是下单了,把我们要下单参数拼接成xml的形式去发送接口,这里主要是签名很重要
签名:
1.签名算法
签名生成的通用步骤如下:

第一步,设所有发送或者接收到的数据为集合M,将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA。

特别注意以下重要规则:

 ◆ 参数名ASCII码从小到大排序(字典序);
 ◆ 如果参数的值为空不参与签名;
 ◆ 参数名区分大小写;
 ◆ 验证调用返回或微信主动通知签名时,传送的sign参数不参与签名,将生成的签名与该sign值作校验。
 ◆ 微信接口可能增加字段,验证签名时必须支持增加的扩展字段
第二步,在stringA最后拼接上key=(API密钥的值)得到stringSignTemp字符串,并对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为大写,得到sign值signValue。
假设传送的参数如下:

appid: wxd930ea5d5a258f4f

mch_id: 10000100

device_info: 1000

body: test

nonce_str: ibuaiVcKdpRxkhJA

第一步:对参数按照key=value的格式,并按照参数名ASCII字典序排序如下:

stringA=”appid=wxd930ea5d5a258f4f&body=test&device_info=1000&mch_id=10000100&nonce_str=ibuaiVcKdpRxkhJA”;

第二步:拼接API密钥:

stringSignTemp=”stringA&key=192006250b4c09247ec02edce69f6a2d”

sign=MD5(stringSignTemp).toUpperCase()=”9A0A8659F005D6984697E2CA0A9CF3B7”

网友的整理:
小程序
这样我们就得到了签名
我们下单成功后微信会给我们预支付交易会话标识 这个很主要,拿这个在进行一次签名加密,
因为我们调用支付的接口中需要一个签名,支付成功后就会调用我们下单时所传的回调地址,我们在回调方法中判断用户是否支付成功,进行业务处理

代码实例:
OrderController.java

    /**     * 下单:     * @param openid 用户openid     * @param money 金额     * @return map     */    @RequestMapping("/createOrder")    @ResponseBody    public Map<String,String>createOrder(String openid,int money){        logger.info("用户的openid:-------->"+openid+"下单的金额:-------------->"+money);        String mch_id= WeChatTool.mch_id; //商户号        String today = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());        String WXPay= WXPayUtil.createCode(8);        String out_trade_no=mch_id+today+WXPay;//生成订单号    Map<String,String> result=new HashMap<String,String>();    //去Service层中去生成签名,用户openid out_trade_no订单号  money支付的金额        String formData=orderService.getopenid(openid,out_trade_no,money);    //在servlet层中生成签名成功后,把下单所要的参数以xml的格式拼接,发送下单接口        String httpResult = HttpUtils.httpXMLPost(WeChatTool.createOrderUrl,formData);        try {    //xml转换成Map对象或者值            Map<String, String> resultMap = WXPayUtil.xmlToMap(httpResult);            result.put("package", "prepay_id=" + resultMap.get("prepay_id")); //这里是拿下单成功的微信交易号去拼接,因为在下面的接口中必须要这个样子            result.put("nonceStr",resultMap.get("nonce_str")); //随机字符串        } catch (Exception e) {            e.printStackTrace();        }        String times= WXPayUtil.getCurrentTimestamp()+""; //获取当前时间        result.put("timeStamp",times); //当前时间戳    //生成调用支付接口要的签名        Map<String, String> packageParams = new HashMap<String ,String>();        packageParams.put("appId", WeChatTool.wxspAppid);        packageParams.put("signType", WeChatTool.sign_type);        packageParams.put("nonceStr",result.get("nonceStr")+"");        packageParams.put("timeStamp",times);        packageParams.put("package", result.get("package")+"");//商户订单号        String sign="";        try {            sign= WXPayUtil.generateSignature(packageParams, WeChatTool.sercet_key); //生成签名:        } catch (Exception e) {            e.printStackTrace();        }        result.put("paySign",sign);        logger.info("签名成功----->"+result.get("paySign"));        return result; //所有的参数放进map中保存发送到小程序页面中,去调用微信支付接口    }

OrderServiceImpl.java:

/**     * 统一下单     * @param openid 用户标识     * @param out_trade_no 订单号     * @param total_fee 金额     * @return String     */    @Override    public String getopenid(String openid,String out_trade_no,int total_fee) {        //下单的金额,因为在微信支付中默认是分所以要这样处理        Integer total_fees=total_fee*100;        微信下单的金额是String类型的所以要转换类型        String money=total_fees.toString();        String nonceStr=WXPayUtil.generateUUID(); //设置UUID作为随机字符串        Map<String ,String> map = new HashMap<String ,String>();        map.put("appid",WeChatTool.wxspAppid); //商户appid        map.put("mch_id", WeChatTool.mch_id);//商户号        map.put("nonce_str",nonceStr); //随机数        map.put("body","大米");//商户名称        map.put("out_trade_no",out_trade_no);//商户订单号        map.put("total_fee",money);//下单金额        map.put("spbill_create_ip", "127.0.0.1");//终端IP            map.put("notify_url",https://xxxx/xxxxx/notify.do);//回调地址 这里的接口必须是在线上用户支付成功才能收到微信发送的信息        map.put("trade_type","JSAPI");//交易类型            map.put("openid",openid+"");//用户openid            map.put("sign_type","MD5");//加密类型        String sign="";        try {        sign= WXPayUtil.generateSignature(map, WeChatTool.sercet_key); //生成sign签名WeChatTool.sercet_key是商户的支付秘钥        } catch (Exception e) {            e.printStackTrace();        }        //拼接成xml的格式,这里的参数必须要和上面的一致,并且每次下单的订单号不能一致        String formData="<xml>";        formData += "<appid>"+ WeChatTool.wxspAppid+"</appid>";         formData += "<mch_id>"+ WeChatTool.mch_id+"</mch_id>";         formData += "<nonce_str>"+nonceStr+"</nonce_str>";        formData += "<body>"+WeChatTool.month+"</body>";        formData += "<out_trade_no>"+out_trade_no +"</out_trade_no>";         formData += "<total_fee>"+money+"</total_fee>";         formData += "<spbill_create_ip>"+"127.0.0.1"+"</spbill_create_ip>";         formData += "<notify_url>"+WeChatTool.notify_url+"</notify_url>";         formData += "<trade_type>"+WeChatTool.trade_type+"</trade_type>";        formData += "<openid>"+openid+"</openid>"; //appid        formData += "<sign_type>"+WeChatTool.sign_type+"</sign_type>";        formData += "<sign>"+sign+"</sign>"; //签名算法        formData += "</xml>";        return formData;    }

工具类:
WXPayUtil.java:

import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.w3c.dom.Node;import org.w3c.dom.NodeList;import com.youquan.utli.WXPayConstants.SignType;import javax.crypto.Mac;import javax.crypto.spec.SecretKeySpec;import javax.xml.parsers.DocumentBuilder;import javax.xml.parsers.DocumentBuilderFactory;import javax.xml.transform.OutputKeys;import javax.xml.transform.Transformer;import javax.xml.transform.TransformerFactory;import javax.xml.transform.dom.DOMSource;import javax.xml.transform.stream.StreamResult;import java.io.ByteArrayInputStream;import java.io.InputStream;import java.io.StringWriter;import java.security.MessageDigest;import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.*;/** * <p>User: qrn * <p>Date: 14-1-28 * <p>Version: 1.0 * 描述: 工具类 */public class WXPayUtil {    /**     *  XML格式字符串转换为Map     * @param strXML XML字符串     * @return  Map XML数据转换后的Map     * @see Exception     */    public static Map<String, String> xmlToMap(String strXML) throws Exception {        try {            Map<String, String> data = new HashMap<String, String>();            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();            InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));            org.w3c.dom.Document doc = documentBuilder.parse(stream);            doc.getDocumentElement().normalize();            NodeList nodeList = doc.getDocumentElement().getChildNodes();            for (int idx = 0; idx < nodeList.getLength(); ++idx) {                Node node = nodeList.item(idx);                if (node.getNodeType() == Node.ELEMENT_NODE) {                    org.w3c.dom.Element element = (org.w3c.dom.Element) node;                    data.put(element.getNodeName(), element.getTextContent());                }            }            try {                stream.close();            } catch (Exception ex) {                // do nothing            }            return data;        } catch (Exception ex) {            WXPayUtil.getLogger().warn("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML);            throw ex;        }    }    /**     * 将Map转换为XML格式的字符串     * @param data Map类型数据     * @return   XML格式的字符串     * @see Exception     */    public static String mapToXml(Map<String, String> data) throws Exception {        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();        DocumentBuilder documentBuilder= documentBuilderFactory.newDocumentBuilder();        org.w3c.dom.Document document = documentBuilder.newDocument();        org.w3c.dom.Element root = document.createElement("xml");        document.appendChild(root);        for (String key: data.keySet()) {            String value = data.get(key);            if (value == null) {                value = "";            }            value = value.trim();            org.w3c.dom.Element filed = document.createElement(key);            filed.appendChild(document.createTextNode(value));            root.appendChild(filed);        }        TransformerFactory tf = TransformerFactory.newInstance();        Transformer transformer = tf.newTransformer();        DOMSource source = new DOMSource(document);        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");        transformer.setOutputProperty(OutputKeys.INDENT, "yes");        StringWriter writer = new StringWriter();        StreamResult result = new StreamResult(writer);        transformer.transform(source, result);        String output = writer.getBuffer().toString(); //.replaceAll("|r", "");        try {            writer.close();        }        catch (Exception ex) {        }        return output;    }    /**     * 生成6位或10位随机数 param codeLength(多少位)     * @param codeLength 参数     * @return  String     */    public static String createCode(int codeLength) {        String code = "";        for (int i = 0; i < codeLength; i++) {            code += (int) (Math.random() * 9);        }        return code;    }    /**     * 生成带有 sign 的 XML 格式字符串     * @param data Map类型数据     * @param key API密钥     * @see Exception     * @return String 含有sign字段的XML     */    public static String generateSignedXml(final Map<String, String> data, String key) throws Exception {        return generateSignedXml(data, key, SignType.MD5);    }    /**     * 生成带有 sign 的 XML 格式字符串     *     * @param data Map类型数据     * @param key API密钥     * @param signType 签名类型     * @see Exception     * @return 含有sign字段的XML     */    public static String generateSignedXml(final Map<String, String> data, String key, SignType signType) throws Exception {        String sign = generateSignature(data, key, signType);        data.put(WXPayConstants.FIELD_SIGN, sign);        return mapToXml(data);    }    /**     * 判断签名是否正确     * @param xmlStr XML格式数据     * @param key  API密钥     * @return  boolean 签名是否正确     * @see Exception     */    public static boolean isSignatureValid(String xmlStr, String key) throws Exception {        Map<String, String> data = xmlToMap(xmlStr);        if (!data.containsKey(WXPayConstants.FIELD_SIGN) ) {            return false;        }        String sign = data.get(WXPayConstants.FIELD_SIGN);        return generateSignature(data, key).equals(sign);    }    /**     * 判断签名是否正确,必须包含sign字段,否则返回false。使用MD5签名。     * @param data Map类型数据     * @param key API密钥     * @return  boolean 签名是否正确     * @see Exception     */    public static boolean isSignatureValid(Map<String, String> data, String key) throws Exception {        return isSignatureValid(data, key, SignType.MD5);    }    /**     * 判断签名是否正确,必须包含sign字段,否则返回false。     *     * @param data Map类型数据     * @param key API密钥     * @param signType 签名方式     * @return boolean 签名是否正确     * @exception Exception     */    public static boolean isSignatureValid(Map<String, String> data, String key, SignType signType) throws Exception {        if (!data.containsKey(WXPayConstants.FIELD_SIGN) ) {            return false;        }        String sign = data.get(WXPayConstants.FIELD_SIGN);        return generateSignature(data, key, signType).equals(sign);    }    /**     * 生成签名     * @param data  待签名数据     * @param key  API密钥     * @return String     * @see Exception     */    public static String generateSignature(final Map<String, String> data, String key) throws Exception {        return generateSignature(data, key, SignType.MD5);    }    /**     * 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。     *     * @param data 待签名数据     * @param key API密钥     * @param signType 签名方式     * @return  String 签名     * @see Exception     */    public static String generateSignature(final Map<String, String> data, String key, SignType signType) throws Exception {        Set<String> keySet = data.keySet();        String[] keyArray = keySet.toArray(new String[keySet.size()]);        Arrays.sort(keyArray);        StringBuilder sb = new StringBuilder();        for (String k : keyArray) {            if (k.equals(WXPayConstants.FIELD_SIGN)) {                continue;            }            if (data.get(k).trim().length() > 0) // 参数值为空,则不参与签名                sb.append(k).append("=").append(data.get(k).trim()).append("&");        }        sb.append("key=").append(key);        if (SignType.MD5.equals(signType)) {            return MD5(sb.toString()).toUpperCase();        }        else if (SignType.HMACSHA256.equals(signType)) {            return HMACSHA256(sb.toString(), key);        }        else {            throw new Exception(String.format("Invalid sign_type: %s", signType));        }    }    /**     * 获取随机字符串 Nonce Str     *     * @return String 随机字符串     */    public static String generateNonceStr() {        return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);    }    /**     *     *     * @param data 待处理数据     * @return String MD5结果     * @see Exception     */    public static String MD5(String data) throws Exception {        MessageDigest md = MessageDigest.getInstance("MD5");        byte[] array = md.digest(data.getBytes("UTF-8"));        StringBuilder sb = new StringBuilder();        for (byte item : array) {            sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));        }        return sb.toString().toUpperCase();    }    /**     * 生成 HMACSHA256     * @param da

版权声明

即速应用倡导尊重与保护知识产权。如发现本站文章存在版权问题,烦请提供版权疑问、身份证明、版权证明、联系方式等发邮件至197452366@qq.com ,我们将及时处理。本站文章仅作分享交流用途,作者观点不等同于即速应用观点。用户与作者的任何交易与本站无关,请知悉。

产品经理

手机 : 13312967497

擅长 : 小程序流量变现

扫码领取礼包

最新资讯

热门模板

  • 头条
  • 搜狐
  • 微博
  • 百家
  • 一点资讯
  • 知乎