微信小程序> Java实现小程序微信支付

Java实现小程序微信支付

浏览量:1110 时间: 来源:稀飯丶

小程序支付流程交互图:

进入小程序,下单,请求下单支付,调用小程序登录API来获取Openid,生成商户订单

// pages/pay/pay.js var app = getApp(); Page({     data: {},     onLoad: function (options) {         // 页面初始化 options为页面跳转所带来的参数     },     /* 微信支付 */     wxpay: function () {         var that = this;        //登陆获取code         wx.login({             success: function (res) {                 //获取openid                 that.getOpenId(res.code);             }         });     },     getOpenId: function (code) {         var that = this;         wx.request({             url: "https://api.weixin.qq.com/sns/jscode2session?appid=小程序appid&secret=小程序Secret&js_code=" + code + "&grant_type=authorization_code",             data: {},             method: 'GET',             success: function (res) {                 console.log(res.data);                 that.generateOrder(res.data.openid);             },             fail: function () {                 // fail             },             complete: function () {                 // complete             }         })     },     /**生成商户订单 */     generateOrder: function (openid) {         var that = this;         //统一支付         wx.request({             url: 'http://localhost:9090/weixin/payment.do',             method: 'GET',             data: {                 total_fee: '666', //金额,注意以分为单位                body: '茅台', //产品简单描述                attach:'广州分店' //附加数据            },             success: function (res) {                 var pay = res.data                 //发起支付                 var timeStamp = pay[0].timeStamp;                 var packages = pay[0].package;                 var paySign = pay[0].paySign;                 var nonceStr = pay[0].nonceStr;                 var param = { "timeStamp": timeStamp, "package": packages, "paySign": paySign, "signType": "MD5", "nonceStr": nonceStr };                 that.pay(param);             },         })     },     /* 支付   */     pay: function (param) {         wx.requestPayment({             timeStamp: param.timeStamp,             nonceStr: param.nonceStr,             package: param.package,             signType: param.signType,             paySign: param.paySign,             success: function (res) {                 wx.navigateBack({                     delta: 1, // 回退前 delta(默认为1) 页面                     success: function (res) {                         wx.showToast({                             title: '支付成功',                             icon: 'success',                             duration: 2000                         })                     },                     fail: function () {                         // fail                     },                     complete: function () {                         // complete                     }                 })             },             fail: function (res) {                 // fail                 console.log("支付失败");             },             complete: function () {                 // complete                 console.log("pay complete");             }         })     } }) 

调用支付统一下单API来获取prepay_id,并将小程序调起支付数据需要签名的字段appId,timeStamp,nonceStr,package再次签名

后台代码

package com.card.mp.controller;import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.JSONObject;import com.card.dto.PaymentDto;import com.card.framework.utils.*;import org.dom4j.Document;import org.dom4j.DocumentException;import org.dom4j.Element;import org.dom4j.io.SAXReader;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.ResponseBody;import javax.servlet.http.HttpServletRequest;import java.io.ByteArrayInputStream;import java.io.InputStream;import java.io.UnsupportedEncodingException;import java.text.SimpleDateFormat;import java.util.Date;import java.util.HashMap;import java.util.List;import java.util.Map;@Controllerpublic class WeiXinPaymentController extends BaseController {    private final String mch_id = "填写商户号";//商户号    private final String spbill_create_ip = "填写终端IP";//终端IP    private final String notify_url = "域名/weixin/paycallback.do";//通知地址    private final String trade_type = "JSAPI";//交易类型    private final String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";//统一下单API接口链接    private final String key = "&key=填写商户支付密钥"; // 商户支付密钥    private final String appid = "填写小程序AppId";    /**     *     * @param openId     * @param total_fee 订单总金额,单位为分。     * @param body  商品简单描述,该字段请按照规范传递。 例:腾讯充值中心-心悦会员充值     * @param attach    附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用。 例:广州分店     * @return     * @throws UnsupportedEncodingException     * @throws DocumentException     */    @RequestMapping("/weixin/payment.do")    @ResponseBody    public JSONObject payment(@RequestParam(required = true) String openId, @RequestParam(required = true)String total_fee, @RequestParam(required = false) String body, @RequestParam(required = false) String attach) throws UnsupportedEncodingException, DocumentException {        JSONObject JsonObject = new JSONObject() ;        body = new String(body.getBytes("UTF-8"),"ISO-8859-1");        String nonce_str = UUIDHexGenerator.generate();//随机字符串        String today = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());        String code = PayUtil.createCode(8);        String out_trade_no = mch_id + today + code;//商户订单号        String openid = openId;//用户标识        PaymentDto paymentPo = new PaymentDto();        paymentPo.setAppid(appid);        paymentPo.setMch_id(mch_id);        paymentPo.setNonce_str(nonce_str);        String newbody = new String(body.getBytes("ISO-8859-1"),"UTF-8");//以utf-8编码放入paymentPo,微信支付要求字符编码统一采用UTF-8字符编码        paymentPo.setBody(newbody);        paymentPo.setOut_trade_no(out_trade_no);        paymentPo.setTotal_fee(total_fee);        paymentPo.setSpbill_create_ip(spbill_create_ip);        paymentPo.setNotify_url(notify_url);        paymentPo.setTrade_type(trade_type);        paymentPo.setOpenid(openid);        // 把请求参数打包成数组        MapString, Object sParaTemp = new HashMap();        sParaTemp.put("appid", paymentPo.getAppid());        sParaTemp.put("mch_id", paymentPo.getMch_id());        sParaTemp.put("nonce_str", paymentPo.getNonce_str());        sParaTemp.put("body",  paymentPo.getBody());        sParaTemp.put("out_trade_no", paymentPo.getOut_trade_no());        sParaTemp.put("total_fee",paymentPo.getTotal_fee());        sParaTemp.put("spbill_create_ip", paymentPo.getSpbill_create_ip());        sParaTemp.put("notify_url",paymentPo.getNotify_url());        sParaTemp.put("trade_type", paymentPo.getTrade_type());        sParaTemp.put("openid", paymentPo.getOpenid());        // 除去数组中的空值和签名参数        Map sPara = PayUtil.paraFilter(sParaTemp);        String prestr = PayUtil.createLinkString(sPara); // 把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串        //MD5运算生成签名        String mysign = PayUtil.sign(prestr, key, "utf-8").toUpperCase();        paymentPo.setSign(mysign);        //打包要发送的xml        String respXml = XmlUtil.messageToXML(paymentPo);        // 打印respXml发现,得到的xml中有“__”不对,应该替换成“_”        respXml = respXml.replace("__", "_");        String param = respXml;        //String result = SendRequestForUrl.sendRequest(url, param);//发起请求        String result = PayUtil.httpRequest(url, "POST", param);        System.out.println("请求微信预支付接口,返回 result:"+result);        // 将解析结果存储在Map中        Map map = new HashMap();        InputStream in=new ByteArrayInputStream(result.getBytes());        // 读取输入流        SAXReader reader = new SAXReader();        Document document = reader.read(in);        // 得到xml根元素        Element root = document.getRootElement();        // 得到根元素的所有子节点        ListElement elementList = root.elements();        for (Element element : elementList) {            map.put(element.getName(), element.getText());        }        // 返回信息        String return_code = map.get("return_code").toString();//返回状态码        String return_msg = map.get("return_msg").toString();//返回信息        String result_code = map.get("result_code").toString;//返回状态码        System.out.println("请求微信预支付接口,返回 code:" + return_code);        System.out.println("请求微信预支付接口,返回 msg:" + return_msg);        if("SUCCESS".equals(return_code) && "SUCCESS".equals(result_code)){            // 业务结果            String prepay_id = map.get("prepay_id").toString();//返回的预付单信息            String nonceStr = UUIDHexGenerator.generate();            JsonObject.put("nonceStr", nonceStr);            JsonObject.put("package", "prepay_id=" + prepay_id);            Long timeStamp = System.currentTimeMillis() / 1000;            JsonObject.put("timeStamp", timeStamp + "");            String stringSignTemp = "appId=" + appid + "&nonceStr=" + nonceStr + "&package=prepay_id=" + prepay_id + "&signType=MD5&timeStamp=" + timeStamp;            //再次签名            String paySign = PayUtil.sign(stringSignTemp, key, "utf-8").toUpperCase();            JsonObject.put("paySign", paySign);        }        return JsonObject;    }    /**     * 预支付时填写的 notify_url ,支付成功后的回调接口     * @param request     */    @RequestMapping("/weixin/paycallback.do")    @ResponseBody    public void paycallback(HttpServletRequest request) {        try {            MapString, Object dataMap = XmlUtil.parseXML(request);            System.out.println(JSON.toJSONString(dataMap));            //{"transaction_id":"4200000109201805293331420304","nonce_str":"402880e963a9764b0163a979a16e0002","bank_type":"CFT","openid":"oXI6G5Jc4D44y2wixgxE3OPwpDVg","sign":"262978D36A3093ACBE4B55707D6EA7B2","fee_type":"CNY","mch_id":"1491307962","cash_fee":"10","out_trade_no":"14913079622018052909183048768217","appid":"wxa177427bc0e60aab","total_fee":"10","trade_type":"JSAPI","result_code":"SUCCESS","time_end":"20180529091834","is_subscribe":"N","return_code":"SUCCESS"}        } catch (Exception e) {            e.printStackTrace();        }    }}

后台业务逻辑涉及到的工具类及参数封装类 

XmlUtil

package com.card.framework.utils;import com.card.dto.PaymentDto;import com.thoughtworks.xstream.XStream;import com.thoughtworks.xstream.core.util.QuickWriter;import com.thoughtworks.xstream.io.HierarchicalStreamWriter;import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;import com.thoughtworks.xstream.io.xml.XppDriver;import org.dom4j.Document;import org.dom4j.DocumentException;import org.dom4j.Element;import org.dom4j.io.SAXReader;import javax.servlet.http.HttpServletRequest;import java.io.IOException;import java.io.Writer;import java.util.HashMap;import java.util.List;import java.util.Map;public class XmlUtil {    public static MapString,Object parseXML(HttpServletRequest request) throws IOException, DocumentException {        MapString,Object map=new HashMapString,Object();        /* 通过IO获得Document */        SAXReader reader = new SAXReader();        Document doc = reader.read(request.getInputStream());        //得到xml的根节点        Element root=doc.getRootElement();        recursiveParseXML(root,map);        return map;    }    private static void recursiveParseXML(Element root,MapString,Object map){        //得到根节点的子节点列表        ListElement elementList=root.elements();        //判断有没有子元素列表        if(elementList.size()==0){            map.put(root.getName(), root.getTextTrim());        }        else{            //遍历            for(Element e:elementList){                recursiveParseXML(e,map);            }        }    }    private static XStream xstream = new XStream(new XppDriver() {        public HierarchicalStreamWriter createWriter(Writer out) {            return new PrettyPrintWriter(out) {                // 对所有xml节点都增加CDATA标记                boolean cdata = true;                public void startNode(String name, Class clazz) {                    super.startNode(name, clazz);                }                protected void writeText(QuickWriter writer, String text) {                    if (cdata) {                        writer.write(text);                    } else {                        writer.write(text);                    }                }            };        }    });    public static String messageToXML(PaymentDto paymentPo){        xstream.alias("xml",PaymentDto.class);        return xstream.toXML(paymentPo);    }}

PaymentDto  //封装支付参数实体 

package com.card.dto;import java.io.Serializable;public class PaymentDto implements Serializable {    private String appid;//小程序ID    private String mch_id;//商户号    private String device_info;//设备号    private String nonce_str;//随机字符串    private String sign;//签名    private String body;//商品描述    private String detail;//商品详情    private String attach;//附加数据    private String out_trade_no;//商户订单号    private String fee_type;//货币类型    private String spbill_create_ip;//终端IP    private String time_start;//交易起始时间    private String time_expire;//交易结束时间    private String goods_tag;//商品标记    private String total_fee;//总金额    private String notify_url;//通知地址    private String trade_type;//交易类型    private String limit_pay;//指定支付方式    private String openid;//用户标识    public String getAppid() {        return appid;    }    public void setAppid(String appid) {        this.appid = appid;    }    public String getMch_id() {        return mch_id;    }    public void setMch_id(String mch_id) {        this.mch_id = mch_id;    }    public String getNonce_str() {        return nonce_str;    }    public void setNonce_str(String nonce_str) {        this.nonce_str = nonce_str;    }    public String getSign() {        return sign;    }    public void setSign(String sign) {        this.sign = sign;    }    public String getBody() {        return body;    }    public void setBody(String body) {        this.body = body;    }    public String getOut_trade_no() {        return out_trade_no;    }    public void setOut_trade_no(String out_trade_no) {        this.out_trade_no = out_trade_no;    }    public String getTotal_fee() {        return total_fee;    }    public void setTotal_fee(String total_fee) {        this.total_fee = total_fee;    }    public String getNotify_url() {        return notify_url;    }    public void setNotify_url(String notify_url) {        this.notify_url = notify_url;    }    public String getTrade_type() {        return trade_type;    }    public void setTrade_type(String trade_type) {        this.trade_type = trade_type;    }    public String getOpenid() {        return openid;    }    public void setOpenid(String openid) {        this.openid = openid;    }    public String getSpbill_create_ip() {        return spbill_create_ip;    }    public void setSpbill_create_ip(String spbill_create_ip) {        this.spbill_create_ip = spbill_create_ip;    }    public String getDevice_info() {        return device_info;    }    public void setDevice_info(String device_info) {        this.device_info = device_info;    }    public String getDetail() {        return detail;    }    public void setDetail(String detail) {        this.detail = detail;    }    public String getAttach() {        return attach;    }    public void setAttach(String attach) {        this.attach = attach;    }    public String getFee_type() {        return fee_type;    }    public void setFee_type(String fee_type) {        this.fee_type = fee_type;    }    public String getTime_start() {        return time_start;    }    public void setTime_start(String time_start) {        this.time_start = time_start;    }    public String getTime_expire() {        return time_expire;    }    public void setTime_expire(String time_expire) {        this.time_expire = time_expire;    }    public String getGoods_tag() {        return goods_tag;    }    public void setGoods_tag(String goods_tag) {        this.goods_tag = goods_tag;    }    public String getLimit_pay() {        return limit_pay;    }    public void setLimit_pay(String limit_pay) {        this.limit_pay = limit_pay;    }}

PayUtil

package com.card.framework.utils;import org.apache.commons.codec.digest.DigestUtils;import java.io.*;import java.net.HttpURLConnection;import java.net.URL;import java.util.*;public class PayUtil {    /**     * 签名字符串     * @param text 需要签名的字符串     * @param key 密钥     * @param input_charset 编码格式     * @return 签名结果     */    public static String sign(String text, String key, String input_charset) {        text = text + key;        return DigestUtils.md5Hex(getContentBytes(text, input_charset));    }    /**     * 签名字符串     *  @param text 需要签名的字符串     * @param sign 签名结果     * @param key 密钥     * @param input_charset 编码格式     * @return 签名结果     */    public static boolean verify(String text, String sign, String key, String input_charset) {        text = text + key;        String mysign = DigestUtils.md5Hex(getContentBytes(text, input_charset));        return mysign.equals(sign);    }    /**     * @param content     * @param charset     * @return     * @throws UnsupportedEncodingException     */    public static byte[] getContentBytes(String content, String charset) {        if (charset == null || "".equals(charset)) {            return content.getBytes();        }        try {            return content.getBytes(charset);        } catch (UnsupportedEncodingException e) {            throw new RuntimeException("MD5签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset);        }    }    /**     * 生成6位或10位随机数 param codeLength(多少位)     * @return     */    public static String createCode(int codeLength) {        String code = "";        for (int i = 0; i  codeLength; i++) {            code += (int) (Math.random() * 9);        }        return code;    }    private static boolean isValidChar(char ch) {        if ((ch = '0' && ch = '9') || (ch = 'A' && ch = 'Z') || (ch = 'a' && ch = 'z'))            return true;        return (ch = 0x4e00 && ch = 0x7fff) || (ch = 0x8000 && ch = 0x952f);    }    /**     * 除去数组中的空值和签名参数     * @param sArray 签名参数组     * @return 去掉空值与签名参数后的新签名参数组     */    public static Map paraFilter(MapString,Object sArray) {        Map result = new HashMap();        if (sArray == null || sArray.size() = 0) {            return result;        }        for (String key : sArray.keySet()) {            String value = (String) sArray.get(key);            if (value == null || value.equals("") || key.equalsIgnoreCase("sign")                    || key.equalsIgnoreCase("sign_type")) {                continue;            }            result.put(key, value);        }        return result;    }    /**     * 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串     * @param params 需要排序并参与字符拼接的参数组     * @return 拼接后字符串     */    public static String createLinkString(Map params) {        List keys = new ArrayList(params.keySet());        Collections.sort(keys);        String prestr = "";        for (int i = 0; i  keys.size(); i++) {            String key = (String) keys.get(i);            String value = (String) params.get(key);            if (i == keys.size() - 1) {// 拼接时,不包括最后一个&字符                prestr = prestr + key + "=" + value;            } else {                prestr = prestr + key + "=" + value + "&";            }        }        return prestr;    }    /**     *     * @param requestUrl 请求地址     * @param requestMethod 请求方法     * @param outputStr 参数     */    public static String httpRequest(String requestUrl,String requestMethod,String outputStr){        // 创建SSLContext        StringBuffer buffer=null;        try{            URL url = new URL(requestUrl);            HttpURLConnection conn = (HttpURLConnection) url.openConnection();            conn.setRequestMethod(requestMethod);            conn.setDoOutput(true);            conn.setDoInput(true);            conn.connect();            //往服务器端写内容            if(null !=outputStr){                OutputStream os=conn.getOutputStream();                os.write(outputStr.getBytes("utf-8"));                os.close();            }            // 读取服务器端返回的内容            InputStream is = conn.getInputStream();            InputStreamReader isr = new InputStreamReader(is, "utf-8");            BufferedReader br = new BufferedReader(isr);            buffer = new StringBuffer();            String line = null;            while ((line = br.readLine()) != null) {                buffer.append(line);            }        }catch(Exception e){            e.printStackTrace();        }        return buffer.toString();    }    public static String urlEncodeUTF8(String source){        String result=source;        try {            result=java.net.URLEncoder.encode(source, "UTF-8");        } catch (UnsupportedEncodingException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }        return result;    }}  

UUIDHexGenerator  //生成随机数工具类

package com.card.framework.utils;import java.net.InetAddress;public class UUIDHexGenerator {    private static String sep = "";    private static final int IP;    private static short counter = (short) 0;    private static final int JVM = (int) (System.currentTimeMillis()  8);    private static UUIDHexGenerator uuidgen = new UUIDHexGenerator();    static {        int ipadd;        try {            ipadd = toInt(InetAddress.getLocalHost().getAddress());        } catch (Exception e) {            ipadd = 0;        }        IP = ipadd;    }    public static UUIDHexGenerator getInstance() {        return uuidgen;    }    public static int toInt(byte[] bytes) {        int result = 0;        for (int i = 0; i  4; i++) {            result = (result  8) - Byte.MIN_VALUE + bytes[i];//            result = (result  - Byte.MIN_VALUE + (int) bytes);        }        return result;    }    protected static String format(int intval) {        String formatted = Integer.toHexString(intval);        StringBuffer buf = new StringBuffer("00000000");        buf.replace(8 - formatted.length(), 8, formatted);        return buf.toString();    }    protected static String format(short shortval) {        String formatted = Integer.toHexString(shortval);        StringBuffer buf = new StringBuffer("0000");        buf.replace(4 - formatted.length(), 4, formatted);        return buf.toString();    }    protected static int getJVM() {        return JVM;    }    protected synchronized static short getCount() {        if (counter  0) {            counter = 0;        }        return counter++;    }    protected static int getIP() {        return IP;    }    protected static short getHiTime() {        return (short) (System.currentTimeMillis()  32);    }    protected static int getLoTime() {        return (int) System.currentTimeMillis();    }    public static String generate() {        return new StringBuffer(36).append(format(getIP())).append(sep).append(format(getJVM())).append(sep)                .append(format(getHiTime())).append(sep).append(format(getLoTime())).append(sep)                .append(format(getCount())).toString();    }    /**     * @param args     */    public static void main(String[] args) {        String id="";        UUIDHexGenerator uuid = UUIDHexGenerator.getInstance();        /*        for (int i = 0; i  100; i++) {            id = uuid.generate();        }*/        id = generate();        System.out.println(id);    }}


版权声明

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

产品经理

手机 : 13312967497

擅长 : 小程序流量变现

扫码领取礼包

热门模板

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