微信支付接口使用的小案例(非SDK,JAVA)

本文详细解析了微信支付JSAPI统一下单接口的实现过程,包括参数设置、签名生成、请求发送及支付结果验证等关键步骤,帮助开发者快速掌握微信支付的集成流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述
1.控制器

/**
* 后台发给微信服务器(放工具内,实例化然后调用)
* 统一下单
* @param request
* @param response
* @return
*/
@RequestMapping(value = "/wechatPay")
public @ResponseBody
public JSONObject createUnifiedOrder(HttpServletRequest request, 
									HttpServletResponse response) {
   System.out.println("微信 统一下单 接口调用");
   //设置最终返回对象
   JSONObject resultJson = new JSONObject();
	
	//设置商户订单号
   String outTradeNo = request.getParameter("orderNum");
   //接受参数(金额)
   String amount = request.getParameter("amount");
   //接受参数(openid)
   String openid = request.getParameter("openid");
   
   //接口调用总金额单位为分换算一下(测试金额改成1,单位为分则是0.01,
   //根据自己业务场景判断是转换成float类型还是int类型)
   //String amountFen = Integer.valueOf((Integer.parseInt(amount)*100)).toString();
   //String amountFen = Float.valueOf((Float.parseFloat(amount)*100)).toString();
   //String amountFen = "1";
   
   //创建hashmap(用户获得签名)
   SortedMap<String, String> paraMap = new TreeMap<String, String>();
   //设置body变量 (支付成功显示在微信支付 商品详情中)
   String body = "微信支付JSAPI测试";
   //设置随机字符串,自己用随机函数(如UUID类)生成的
   String nonceStr = UUID.randomUUID() + RandomStringUtils.randomAlphabetic(30);
   

   //设置请求参数(小程序ID)
   paraMap.put("appid", WeChatPaymentUtil.APPLYID);
   //设置请求参数(商户号)
   paraMap.put("mch_id", WeChatPaymentUtil.MCHID);
   //设置请求参数(随机字符串)
   paraMap.put("nonce_str", nonceStr);
   //设置请求参数(商品描述)
   paraMap.put("body", body);
   //设置请求参数(商户订单号)
   paraMap.put("out_trade_no", outTradeNo);
   //设置请求参数(总金额)
   paraMap.put("total_fee", amount);
   //设置请求参数(终端IP)
   paraMap.put("spbill_create_ip", WeChatPaymentUtil.getIpAddress(request));
   //设置请求参数(通知地址)  ——>  payCallback(request,response)
   paraMap.put("notify_url", request.getScheme()+request.getServerName()
   +request.getServerPort()+"/项目根路径/weChatPayCallback");
   //设置请求参数(交易类型)
   paraMap.put("trade_type", "JSAPI");
   //设置请求参数(openid)(在接口文档中 该参数 是否必填项 但是一定要注意 如果交易类型设置
   //成'JSAPI'则必须传入openid)
   paraMap.put("openid", openid);
   //调用逻辑传入参数按照字段名的 ASCII 码从小到大排序(字典序)
   String stringA = formatUrlMap(paraMap, false, false);

   //第二步,在stringA最后拼接上key得到stringSignTemp字符串,并对stringSignTemp进行MD5运算,
   //再将得到的字符串所有字符转换为大写,得到sign值signValue。(签名)
   String sign = MD5Util.MD5Encode(stringA+"&key="+KEY,null).toUpperCase();

   //签名和总金额要存一下,用于后面支付结果的验证
   request.getSession().setAttribute("weChatAmount",amountFen);

   //将参数 编写XML格式
   StringBuffer paramBuffer = new StringBuffer();
   paramBuffer.append("<xml>");
   paramBuffer.append("<appid>"+APPLYID+"</appid>");
   paramBuffer.append("<mch_id>"+MCHID+"</mch_id>");
   paramBuffer.append("<nonce_str>"+paraMap.get("nonce_str")+"</nonce_str>");
   paramBuffer.append("<sign>"+sign+"</sign>");
   paramBuffer.append("<body>"+body+"</body>");
   paramBuffer.append("<out_trade_no>"+paraMap.get("out_trade_no")+"</out_trade_no>");
   paramBuffer.append("<total_fee>"+paraMap.get("total_fee")+"</total_fee>");
   paramBuffer.append("<spbill_create_ip>"+paraMap.get("spbill_create_ip")
   +"</spbill_create_ip>");
   paramBuffer.append("<notify_url>"+paraMap.get("notify_url")+"</notify_url>");
   paramBuffer.append("<trade_type>"+paraMap.get("trade_type")+"</trade_type>");
   paramBuffer.append("<openid>"+paraMap.get("openid")+"</openid>");
   paramBuffer.append("</xml>");
   
   try {
       //发送请求(POST)(获得数据包ID)(这有个注意的地方 如果不转码成ISO8859-1
       //则会告诉你body不是UTF8编码 就算你改成UTF8编码也一样不好使 所以修改成ISO8859-1)
       Map<String,String> map = 
       WeChatPaymentUtil.doXMLParse(
       WeChatPaymentUtil.getRemotePortData(URL, new String(paramBuffer.toString().getBytes()
       , "ISO8859-1")));
       //应该创建 支付表数据
       if(map!=null){
           //查找本地数据库的订单支付信息
           //如果等于空 则证明是第一次支付,代码省略
           if(   ){
               //创建支付信息对象
			   //代码省略
               
               System.out.println("微信 统一下单 接口调用成功 并且新增支付信息成功");
               return WeChatPaymentUtil.generateSignature(request,map.get("prepay_id"));

           }else{
               //判断 是否等于一条
               if(   == 1){
                   //更新支付信息对象
                   //代码省略
                   
                   System.out.println("微信 统一下单 接口调用成功 修改支付信息成功");
                   return WeChatPaymentUtil.generateSignature(request,map.get("prepay_id"));
               }
           }
       }
   } catch (UnsupportedEncodingException e) {
       System.out.println("微信 统一下单 异常:"+e.getMessage());
       e.printStackTrace();
   } catch (Exception e) {
       System.out.println("微信 统一下单 异常:"+e.getMessage());
       e.printStackTrace();
   }
   System.out.println("微信 统一下单 失败");
   return resultJson;
}

/**
* 通知地址指向的控制器方法(Controller中的方法被微信服务器访问)->
* 接收服务器来的支付结果(这个回调 如果不返回给微信服务器 是否成功回调标示 则会一直回调8次 
* 一直到返回成功标示位置)
* @param request
* @param response
*/
@RequestMapping(value = "/weChatPayCallback")
public @ResponseBody
public void payCallback(HttpServletRequest request,HttpServletResponse response) {
   System.out.println("微信回调接口方法 start");
   System.out.println("微信回调接口 操作逻辑 start");
   String inputLine = "";
   String notityXml = "";

   try {
       PrintWriter writer = response.getWriter();

       while((inputLine = request.getReader().readLine()) != null){
           notityXml += inputLine;
       }
       //关闭流
       request.getReader().close();
       System.out.println("微信回调内容信息:"+notityXml);
       //解析成Map
       Map<String,String> map = doXMLParse(notityXml);
       //检查对应业务数据的状态,判断 支付是否成功
       if("SUCCESS".equals(map.get("result_code"))){
           System.out.println("微信回调返回是否支付成功:是");
           //获得 返回的商户订单号
           String outTradeNo = map.get("out_trade_no");
           System.out.println("微信回调返回商户订单号:"+outTradeNo);

           //对于支付结果通知的内容一定要做签名验证,并校验返回的订单金额是否与商户侧的订单金额一致
           //防止数据泄漏导致出现“假通知”,造成资金损失
           String sign = request.getSession().getAttribute("weChatSign").toString();
           String amount = request.getSession().getAttribute("weChatAmount").toString();
           if (sign.equals(map.get("sign"))&&amount.equals(map.get("total_fee"))) {
               //访问数据库,修改订单状态
              //代码省略
               if (订单状态为未付款) {
                   //修改支付状态
				   //代码省略

                    //封装 返回值
                    StringBuffer buffer = new StringBuffer();
                    buffer.append("<xml>");
                    buffer.append("<return_code>SUCCESS</return_code>");
                    buffer.append("<return_msg>OK</return_msg>");
                    buffer.append("</xml>");
                    //给微信服务器返回 成功标示 否则会一直询问 咱们服务器 是否回调成
                    //返回
                    writer.print(buffer.toString());
               }
           }
           StringBuffer buffer = new StringBuffer();
           buffer.append("<xml>");
           buffer.append("<return_code>FAIL</return_code>");
           buffer.append("<return_msg>签名失败或金额不符</return_msg>");
           buffer.append("</xml>");
           writer.print(buffer.toString());
       }
       StringBuffer buffer = new StringBuffer();
       buffer.append("<xml>");
       buffer.append("<return_code>FAIL</return_code>");
       buffer.append("<return_msg>支付失败</return_msg>");
       buffer.append("</xml>");
       writer.print(buffer.toString());
   } catch (IOException e) {
       e.printStackTrace();
   } catch (Exception e) {
       e.printStackTrace();
   }
}

2.工具类

import net.sf.json.JSONObject;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang.StringUtils;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;

import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.*;

public class WeChatPaymentUtil {
    //公众账号ID
    private final static String APPLYID ="";
    //商户号
    private final static String MCHID = "";
    //key为商户平台设置的密钥key
    private final static String KEY = "";
    //统一订单接口
    private final static String URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
    
    /**
     * 方法名: getRemotePortData
     * 描述: 发送远程请求 获得代码示例
     * 参数:  @param urls 访问路径
     * 参数:  @param param 访问参数-字符串拼接格式, 例:port_d=10002&port_g=10007&country_a=
     * 返回类型: String
     */
    public static String getRemotePortData(String urls, String param){

        try {
            URL url = new URL(urls);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            // 设置连接超时时间
            conn.setConnectTimeout(30000);
            // 设置读取超时时间
            conn.setReadTimeout(30000);
            conn.setRequestMethod("POST");
            if(StringUtils.isNotBlank(param)) {
                conn.setRequestProperty("X-Requested-With", "XMLHttpRequest");// 主要参数
            }
            // 需要输出
            conn.setDoInput(true);
            // 需要输入
            conn.setDoOutput(true);
            // 设置是否使用缓存
            conn.setUseCaches(false);
            // 设置请求属性
            conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            conn.setRequestProperty("Connection", "Keep-Alive");// 维持长连接
            conn.setRequestProperty("Charset", "UTF-8");

            if(StringUtils.isNotBlank(param)) {
                // 建立输入流,向指向的URL传入参数
                DataOutputStream dos=new DataOutputStream(conn.getOutputStream());
                dos.writeBytes(param);
                dos.flush();
                dos.close();
            }
            // 输出返回结果
            InputStream input = conn.getInputStream();
            int resLen =0;
            byte[] res = new byte[1024];
            StringBuilder sb=new StringBuilder();
            while((resLen=input.read(res))!=-1){
                sb.append(new String(res, 0, resLen));
            }
            return sb.toString();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
     * @param strxml
     * @return
     * @throws
     * @throws IOException
     */
    @SuppressWarnings("rawtypes")
    private Map<String,String> doXMLParse(String strxml) throws Exception {
        if(null == strxml || "".equals(strxml)) {
            return null;
        }

        Map<String,String> m = new HashMap<String,String>();
        InputStream in = String2Inputstream(strxml);
        SAXBuilder builder = new SAXBuilder();
        Document doc = builder.build(in);
        Element root = doc.getRootElement();
        List list = root.getChildren();
        Iterator it = list.iterator();
        while(it.hasNext()) {
            Element e = (Element) it.next();
            String k = e.getName();
            String v = "";
            List children = e.getChildren();
            if(children.isEmpty()) {
                v = e.getTextNormalize();
            } else {
                v = getChildrenText(children);
            }

            m.put(k, v);
        }

        //关闭流
        in.close();

        return m;
    }

    public static  InputStream String2Inputstream(String str) {
        return new ByteArrayInputStream(str.getBytes());
    }

    /**
     * 获取子结点的xml
     * @param children
     * @return String
     */
    public static String getChildrenText(List children) {
        StringBuffer sb = new StringBuffer();
        if(!children.isEmpty()) {
            Iterator it = children.iterator();
            while(it.hasNext()) {
                Element e = (Element) it.next();
                String name = e.getName();
                String value = e.getTextNormalize();
                List list = e.getChildren();
                sb.append("<" + name + ">");
                if(!list.isEmpty()) {
                    sb.append(getChildrenText(list));
                }
                sb.append(value);
                sb.append("</" + name + ">");
            }
        }

        return sb.toString();
    }

    /**
     * @Title: getIpAddress
     * @Description: 获取客户端真实IP地址
     * @author yihj
     * @param @param request
     * @param @param response
     * @param @return    参数
     * @return String    返回类型
     * @throws
     */
    public static String getIpAddress(HttpServletRequest request) {
        // 避免反向代理不能获取真实地址, 取X-Forwarded-For中第一个非unknown的有效IP字符串
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
            ip = request.getRemoteAddr();
        }
        return ip;
    }



    /**
     * 后台生成签名
     * @param request
     * @param
     * @return  返回给小程序
     */
    public static JSONObject generateSignature(HttpServletRequest request,
                                        String prepayid) {
        System.out.println("微信 支付接口生成签名 方法开始");
        //实例化返回对象
        JSONObject resultJson = new JSONObject();

        //获得参数(微信统一下单接口生成的prepay_id )
        String prepayId = prepayid;
        //创建 时间戳
        String timeStamp = Long.valueOf(System.currentTimeMillis()).toString();
        //创建 随机串
        String nonceStr = UUID.randomUUID() + RandomStringUtils.randomAlphabetic(30);
        //创建 MD5
        String signType = "MD5";

        //创建hashmap(用户获得签名)
        SortedMap<String, String> paraMap = new TreeMap<String, String>();
        //设置(小程序ID)(这块一定要是大写)
        paraMap.put("appId", APPLYID);
        //设置(时间戳)
        paraMap.put("timeStamp", timeStamp);
        //设置(随机串)
        paraMap.put("nonceStr", nonceStr);
        //设置(数据包)
        paraMap.put("package", "prepay_id="+prepayId);
        //设置(签名方式)
        paraMap.put("signType", signType);


        //调用逻辑传入参数按照字段名的 ASCII 码从小到大排序(字典序)
        String stringA = formatUrlMap(paraMap, false, false);
        //第二步,在stringA最后拼接上key得到stringSignTemp字符串,并对stringSignTemp进行MD5运算,
        //再将得到的字符串所有字符转换为大写,得到sign值signValue。(签名)
        String sign = MD5Util.MD5Encode(stringA+"&key="+KEY,null).toUpperCase();

        if(StringUtils.isNotBlank(sign)){
            //签名和总金额要存一下,用于后面支付结果的验证
            request.getSession().setAttribute("weChatSign",sign);

            //返回签名信息
            resultJson.put("paySign", sign);
            //返回签名方式
            resultJson.put("signType", signType);
            //返回随机串(这个随机串是新创建的)
            resultJson.put("nonceStr", nonceStr);
            //返回时间戳
            resultJson.put("timeStamp", timeStamp);
            //返回数据包
            resultJson.put("package", "prepay_id="+prepayId);

            System.out.println("微信 支付接口生成签名 设置返回值");
        }
        System.out.println("微信 支付接口生成签名 方法结束");
        return resultJson;
    }

    /**
     *
     * 方法用途: 对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序),并且生成url参数串
     * 实现步骤:
     *
     * @param paraMap   要排序的Map对象
     * @param urlEncode   是否需要URLENCODE
     * @param keyToLower    是否需要将Key转换为全小写
     *            true:key转化成小写,false:不转化
     * @return
     */
    private static String formatUrlMap(Map<String, String> paraMap, boolean urlEncode
    , boolean keyToLower){
        String buff = "";
        Map<String, String> tmpMap = paraMap;
        try
        {
            List<Map.Entry<String, String>> infoIds = 
            new ArrayList<Map.Entry<String, String>>(tmpMap.entrySet());
            // 对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序)
            Collections.sort(infoIds, new Comparator<Map.Entry<String, String>>()
            {
                @Override
                public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2)
                {
                    return (o1.getKey()).toString().compareTo(o2.getKey());
                }
            });
            // 构造URL 键值对的格式
            StringBuilder buf = new StringBuilder();
            for (Map.Entry<String, String> item : infoIds)
            {
                if (StringUtils.isNotBlank(item.getKey()))
                {
                    String key = item.getKey();
                    String val = item.getValue();
                    if (urlEncode)
                    {
                        val = URLEncoder.encode(val, "utf-8");
                    }
                    if (keyToLower)
                    {
                        buf.append(key.toLowerCase() + "=" + val);
                    } else
                    {
                        buf.append(key + "=" + val);
                    }
                    buf.append("&");
                }

            }
            buff = buf.toString();
            if (buff.isEmpty() == false)
            {
                buff = buff.substring(0, buff.length() - 1);
            }
        } catch (Exception e)
        {
            return null;
        }
        return buff;
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值