[微信支付] SpringMVC开发小案例

最近写了一个微信开发java版的,

先从微信支付官方(https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1)下载demo(java版),

1、服务号相关信息,填上相关的参数


/**
 * 服务号相关信息
 */
public class ConfigUtil {

    
    public final static String APPID = "";
  
    public final static String MCH_ID = "";// 商户号
   
    public final static String API_KEY = "";// API密钥




}

2、因为微信支付的金额单位是元,所以在传输的时候需要将元转换为分,以下是转换包

import java.text.NumberFormat;
import java.text.ParseException;


//金额转换
public class moneyUtil{


    //分转元
    public static String changeY2F(Double amount){
        String currency =  amount.toString();
        int index = currency.indexOf(".");
        int length = currency.length();
        Long amLong = 0l;
        if(index == -1){
            amLong = Long.valueOf(currency+"00");
        }else if(length - index >= 3){
            amLong = Long.valueOf((currency.substring(0, index+3)).replace(".", ""));
        }else if(length - index == 2){
            amLong = Long.valueOf((currency.substring(0, index+2)).replace(".", "")+0);
        }else{
            amLong = Long.valueOf((currency.substring(0, index+1)).replace(".", "")+"00");
        }
        return amLong.toString();
    }


    //元转分
    public static String fenToYuan(String amount){
        NumberFormat format = NumberFormat.getInstance();
        try{
            Number number = format.parse(amount);
            double temp = number.doubleValue() / 100.0;
            format.setGroupingUsed(false);
            // 设置返回的小数部分所允许的最大位数
            format.setMaximumFractionDigits(2);
            amount = format.format(temp);
        } catch (ParseException e){
            e.printStackTrace();
        }
        return amount;
    }

}

3、其中也sign用到了md5加密,还有16位随机字符串,获取ip地址方法

    //md5大写
    public static String MD5Encode(String str) throws NoSuchAlgorithmException, UnsupportedEncodingException {

        StringBuilder sign = new StringBuilder();
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] bytes = md.digest(str.getBytes("utf-8"));

        for (int i = 0; i < bytes.length; i++) {
            String hex = Integer.toHexString(bytes[i] & 0xFF);
            if (hex.length() == 1) {
                sign.append("0");
            }
            sign.append(hex.toUpperCase());
        }
        return sign.toString();
    }


    /**
     * 创建随机字符串16位
     */
    public static String CreateNoncestr() {
        String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        String res = "";
        for (int i = 0; i < 16; i++) {
            Random rd = new Random();
            res += chars.charAt(rd.nextInt(chars.length() - 1));
        }
        return res;
    }

 /**获取ip地址*/
    public static String getIpAddr(HttpServletRequest request) {
        InetAddress addr = null;
        try {
            addr = InetAddress.getLocalHost();
        } catch (UnknownHostException e) {
            return request.getRemoteAddr();
        }
        byte[] ipAddr = addr.getAddress();
        String ipAddrStr = "";
        for (int i = 0; i < ipAddr.length; i++) {
            if (i > 0) {
                ipAddrStr += ".";
            }
            ipAddrStr += ipAddr[i] & 0xFF;
        }
        return ipAddrStr;
    }

4、下单接口,因为自己第一次尝试写,其中有些没有用到方法,代码比较杂乱

//1、下单
    @RequestMapping("/createNative")
    public Object createNative(@RequestParam("outTradeNo") String outTradeNo, @RequestParam("totalFee") String totalFee,
                                                                        @RequestParam("body") String body,HttpServletRequest request)throws Exception {
        //解决中文乱码
        body  = new String(body.getBytes("iso-8859-1"), "utf-8");

        try {
            request.setCharacterEncoding("UTF-8");
            SortedMap<String , String> req = new TreeMap<String, String>();
            req.put("appid", ConfigUtil.APPID);
            req.put("mch_id", ConfigUtil.MCH_ID);

            req.put("out_trade_no", outTradeNo);
            totalFee= moneyUtil.changeY2F(Double.valueOf(totalFee));
            req.put("total_fee", totalFee);
            req.put("body",body);

            // 32位随机字符串
            String random = PayCommonUtil.CreateNoncestr();

            req.put("nonce_str", random);
            // 获取ip地址
            req.put("spbill_create_ip", PayCommonUtil.getIpAddr(request));

            String notify_url = "http://www.huiyouar.com/WeiXinSmpay/WeixinNotify";
            req.put("notify_url",notify_url );
            req.put("trade_type", "NATIVE");

            String sppend = "appid="+ConfigUtil.APPID+"&body="+body+"&mch_id="+ConfigUtil.MCH_ID+
                    "&nonce_str="+random+"&notify_url="+notify_url+"&out_trade_no="+outTradeNo+
                    "&spbill_create_ip="+PayCommonUtil.getIpAddr(request)+
                    "&total_fee="+totalFee+"&trade_type=NATIVE&key="+ConfigUtil.API_KEY;


            String sign = PayCommonUtil.MD5Encode(sppend);
            req.put("sign", sign);


            //生成XML
            String xmlBody = WXPayUtil.generateSignedXml(req, ConfigUtil.API_KEY);

            String sendurl = "https://api.mch.weixin.qq.com/pay/unifiedorder";
            String result = PayCommonUtil.httpsRequest(sendurl, "POST", xmlBody);

            Map<String, String> resultMap = WXPayUtil.xmlToMap(result);

            if(resultMap.get("result_code").equals("SUCCESS")){
                return resultMap.get("code_url");
            }else{
                return JSON.toJSONString(resultMap);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return "请求异常";
        }
    }

以上代码可以就实现微信支付了,

接下来是微信支付查询接口

1、

//3、查询
@SneakyThrows
@RequestMapping("/queryOrder")
public String queryOrder(@RequestParam("out_trade_no") String out_trade_no){

    SortedMap<String, String> parameters = new TreeMap<String, String>();
    parameters.put("appid", ConfigUtil.APPID);
    parameters.put("mch_id", ConfigUtil.MCH_ID);
    // 随机字符串
    String randomString = PayCommonUtil.CreateNoncestr();
    parameters.put("nonce_str", randomString);
    parameters.put("out_trade_no", out_trade_no);

    //签名校验
    String append = "appid="+ConfigUtil.APPID+"&mch_id="+ConfigUtil.MCH_ID+"&nonce_str="+randomString+"&out_trade_no="+out_trade_no+"&key="+ConfigUtil.API_KEY;
    String sign = PayCommonUtil.MD5Encode(append);
    parameters.put("sign", sign);

    //生成XML
    String requestXML = WXPayUtil.generateSignedXml(parameters, ConfigUtil.API_KEY);


    String url = "https://api.mch.weixin.qq.com/pay/orderquery";
    String result = PayCommonUtil.httpsRequest(url, "POST", requestXML);

    //返回结果
    Map<String, String> resultMap = WXPayUtil.xmlToMap(result);
    String return_code =resultMap.get("return_code");
    String result_code =resultMap.get("result_code");
    String trade_state =resultMap.get("trade_state");
    if(return_code.equals("SUCCESS")&&result_code.equals("SUCCESS")&&trade_state.equals("SUCCESS")){
        return "支付成功";
    }else if(return_code.equals("SUCCESS")&&result_code.equals("SUCCESS")&&trade_state.equals("REFUND")){
        return "转入退款 ";
    }else if(return_code.equals("SUCCESS")&&result_code.equals("SUCCESS")&&trade_state.equals("NOTPAY")){
        return "订单未支付 ";
    }else if(return_code.equals("SUCCESS")&&result_code.equals("SUCCESS")&&trade_state.equals("CLOSED")){
        return "订单已关闭 ";
    }else if(return_code.equals("SUCCESS")&&result_code.equals("SUCCESS")&&trade_state.equals("REVOKED")){
        return "已撤销(刷卡支付)  ";
    }else if(return_code.equals("SUCCESS")&&result_code.equals("SUCCESS")&&trade_state.equals("USERPAYING")){
        return "用户支付中  ";
    }else if(return_code.equals("SUCCESS")&&result_code.equals("SUCCESS")&&trade_state.equals("PAYERROR")){
        return "支付失败(其他原因,如银行返回失败)";
    }else if(return_code.equals("SUCCESS")&&result_code.equals("SUCCESS")&&trade_state.equals("PAYERROR")){
        return "支付失败(其他原因,如银行返回失败)";
    }else if(return_code.equals("SUCCESS")&&result_code.equals("SUCCESS")&&trade_state.equals("ACCEPT")){
        return "已接收,等待扣款";
    }else if(return_code.equals("SUCCESS")&&result_code.equals("FAIL")&&resultMap.get("err_code_des").equals("订单不存在")){
        return "订单不存在";
    }else {
        return String.valueOf(resultMap);
    }

}

2、关闭订单接口

//2、关闭订单
@RequestMapping("/closeOrder")
public String closeOrder(@RequestParam("out_trade_no") String out_trade_no) throws Exception {

    SortedMap<String, String> parameters = new TreeMap<String, String>();
    parameters.put("appid", ConfigUtil.APPID);
    parameters.put("mch_id", ConfigUtil.MCH_ID);
    // 随机字符串
    String randomString = PayCommonUtil.CreateNoncestr();
    parameters.put("nonce_str", randomString);
    parameters.put("out_trade_no", out_trade_no);

    String append = "appid="+ConfigUtil.APPID+"&mch_id="+ConfigUtil.MCH_ID+"&nonce_str="+randomString+"&out_trade_no="+out_trade_no+"&key="+ConfigUtil.API_KEY;

    String sign = PayCommonUtil.MD5Encode(append);
    parameters.put("sign", sign);

    //生成XML
    String requestXML = WXPayUtil.generateSignedXml(parameters, ConfigUtil.API_KEY);


    String url = "https://api.mch.weixin.qq.com/pay/closeorder";
    String result = PayCommonUtil.httpsRequest(url, "POST", requestXML);

    Map<String, String> resultMap = WXPayUtil.xmlToMap(result);
    String  resultCode = resultMap.get("return_code");
    if(resultCode.equals("SUCCESS")){
        return "订单取消成功";
    }else{
        return "订单取消失败";
    }


}

 3、退款接口有些麻烦,他需要用到证书,不然会报错,百度查了告诉我是缺少证书,加载进来就可以实现功能了,

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.KeyStore;
import javax.net.ssl.SSLContext;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;


public class CommonUtil {
    private static int socketTimeout = 10000;// 连接超时时间,默认10秒
    private static int connectTimeout = 30000;// 传输超时时间,默认30秒
    private static RequestConfig requestConfig;// 请求器的配置
    private static CloseableHttpClient httpClient;// HTTP请求器

    public static String postData(String url, String xmlObj, String path) {
        try {
            // 加载证书
            initCert(path);
        } catch (Exception e) {
            e.printStackTrace();
        }
        String result = null;
        HttpPost httpPost = new HttpPost(url);
        // 得指明使用UTF-8编码,否则到API服务器XML的中文不能被成功识别
        StringEntity postEntity = new StringEntity(xmlObj, "UTF-8");
        httpPost.addHeader("Content-Type", "text/xml");
        httpPost.setEntity(postEntity);
        // 根据默认超时限制初始化requestConfig
        requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout).build();
        // 设置请求器的配置
        httpPost.setConfig(requestConfig);
        try {
            HttpResponse response = null;
            try {
                response =  httpClient.execute(httpPost);
            }  catch (IOException e) {
                e.printStackTrace();
            }
            HttpEntity entity = response.getEntity();
            try {
                result = EntityUtils.toString(entity, "UTF-8");
            }  catch (IOException e) {
                e.printStackTrace();
            }
        } finally {
            httpPost.abort();
        }
        return result;
    }

    /**
     * 加载证书
     *
     */
    @SuppressWarnings("deprecation")
    private static void initCert(String path) throws Exception {
        // 证书密码,默认为商户ID
        String key = ConfigUtil.MCH_ID;
        // 指定读取证书格式为PKCS12
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        // 读取本机存放的PKCS12证书文件
        FileInputStream instream = new FileInputStream(new File(path));
        try {
            // 指定PKCS12的密码(商户ID)
            keyStore.load(instream, key.toCharArray());
        } finally {
            instream.close();
        }
        SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, key.toCharArray()).build();
        // 指定TLS版本
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                sslcontext, new String[] { "TLSv1" }, null,
                SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
        // 设置httpclient的SSLSocketFactory
        httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
    }

}

退款接口

//4、退款-----证书
@SneakyThrows
@RequestMapping("/refundOrder")
public String refundOrder(@RequestParam("out_trade_no") String out_trade_no,@RequestParam("out_refund_no")  String out_refund_no,
                          @RequestParam("total_fee")  String total_fee,@RequestParam("refund_fee")String refund_fee){

    SortedMap<String, String> parameters = new TreeMap<String, String>();
    parameters.put("appid", ConfigUtil.APPID);
    parameters.put("mch_id", ConfigUtil.MCH_ID);
    // 随机字符串
    String randomString = PayCommonUtil.CreateNoncestr();
    parameters.put("nonce_str", randomString);

    parameters.put("out_trade_no", out_trade_no);
    //退款订单号
    parameters.put("out_refund_no", out_refund_no);
    //退款金额
    total_fee = chang2F.changeY2F(Double.valueOf(total_fee));
    parameters.put("total_fee", total_fee);
    refund_fee = moneyUtil.changeY2F(Double.valueOf(refund_fee));
    parameters.put("refund_fee", refund_fee);

    //签名校验
    String append = "appid="+ConfigUtil.APPID+"&mch_id="+ConfigUtil.MCH_ID+"&nonce_str="+randomString+"&out_refund_no="+out_refund_no +"&out_trade_no="+
                                                        out_trade_no+"&refund_fee="+refund_fee+"&total_fee="+total_fee+"&key="+ConfigUtil.API_KEY;

    String sign = PayCommonUtil.MD5Encode(append);
    parameters.put("sign", sign);

    //生成XML
    String requestXML = WXPayUtil.generateSignedXml(parameters, ConfigUtil.API_KEY);
    String imgPath = "证书目录"+"apiclient_cert.p12";

    String url = "https://api.mch.weixin.qq.com/secapi/pay/refund";

    String result =   CommonUtil.postData(url,requestXML,imgPath);

    Map<String, String> resultMap = WXPayUtil.xmlToMap(result);
    String return_code = resultMap.get("return_code");
    if(return_code.equals("SUCCESS")){
        return "退款申请接收成功";
    }else{
        return JSON.toJSONString(resultMap);
    }

}

 退款查询接口

//5、退款查询
@SneakyThrows
@RequestMapping("/refundqueryOrder")
public String refundqueryOrder(@RequestParam("out_trade_no") String out_trade_no){

    SortedMap<String, String> parameters = new TreeMap<String, String>();
    parameters.put("appid", ConfigUtil.APPID);
    parameters.put("mch_id", ConfigUtil.MCH_ID);
    String ramdomString = PayCommonUtil.CreateNoncestr();
    parameters.put("nonce_str",ramdomString );// 随机字符串
    parameters.put("out_trade_no", out_trade_no);

    String append = "appid="+ConfigUtil.APPID+"&mch_id="+ConfigUtil.MCH_ID+"&nonce_str="+ramdomString+"&out_trade_no="+out_trade_no+"&key="+ConfigUtil.API_KEY;

    String sign = PayCommonUtil.MD5Encode(append);
    parameters.put("sign", sign);

    //生成XML
    String requestXML = WXPayUtil.generateSignedXml(parameters, ConfigUtil.API_KEY);

    String url = "https://api.mch.weixin.qq.com/pay/refundquery";
    String result = PayCommonUtil.httpsRequest(url, "POST", requestXML);

    Map<String, String> resultMap = WXPayUtil.xmlToMap(result);
    return JSON.toJSONString(resultMap);

}

还有一些就是springmvc配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:jee="http://www.springframework.org/schema/jee"
	xmlns:aop="http://www.springframework.org/schema/aop" 
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:util="http://www.springframework.org/schema/util"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
		http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.1.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
		http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
		http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.1.xsd" >
	
	 <!-- 1.扫描Controller的包-->
    <context:component-scan base-package="com.saihui.controller"/>
    <!-- 2.配置视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 2.1 页面前缀 -->
        <property name="prefix" value="/WEB-INF/"/>
        <!-- 2.2 页面后缀 -->
        <property name="suffix" value=".jsp"/>
    </bean>

    <!-- 3.开启mvc注解驱动-->
    <mvc:annotation-driven/>
    
        <mvc:annotation-driven>
        <!--设置响应输出字符集-->
        <mvc:message-converters>
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <property name="supportedMediaTypes">
                    <list>
                        <value>text/html;charset=utf-8</value>
                    </list>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>
</beans>

 web.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

<servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:springmvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

    <!-- Spring字符集过滤器 -->
    <filter>
        <filter-name>SpringEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>SpringEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

因为我下单接口返回的是链接,如果需要生成图片的话,以下是util

package com.saihui.util;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;

import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Hashtable;

public class ImageUtil {
    public static String image(String trl) {
        //二维码宽度
        int width=300;
        //二维码高度
        int height=300;
        //定义二维码图片格式
        String format="png";

        //定义二维码属性
        Hashtable<EncodeHintType,String> his=new Hashtable<EncodeHintType,String>();
        //二维码内容的编码格式
        his.put(EncodeHintType.CHARACTER_SET,"utf-8");
        //定义二维码边距
        his.put(EncodeHintType.MARGIN,"1");


        //创建位矩阵对象并且生成二维码对应的位矩阵对象
        //BarcodeFormat.QR_CODE  生成图片类型为QRCode
        BitMatrix bitMatrix= null;
        try {
            bitMatrix = new MultiFormatWriter().encode(trl, BarcodeFormat.QR_CODE,width,height);
        } catch (Exception e) {
            e.printStackTrace();
        }
        //二维码生成路径
        String path="D:\\";
        //二维码生成名字
        String name="myBlog.png";

        Path path2= Paths.get(path,name);
        //生成二维码
        try {
            MatrixToImageWriter.writeToPath(bitMatrix, format, path2);
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("二维码生成成功");
        return format;
    }


}

 在controller里改成out.write(ImageUtil.image("微信支付返回的链接"));就可以了,把@RestController改成@Controller

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值