NutzCN Logo
分享 终于把微信支付做出来了,分享一下
发布于 2770天前 作者 shusheng 3450 次浏览 复制 上一个帖子 下一个帖子
标签:

统一下单接口

/**
     * pc端扫码支付入口
     * @param toTopicId 帖子id
     * @param money 打赏多少钱
     * @param itype 帖子类型
     * @param flag 支付宝打赏还是微信打赏
     * @return
     */
    public Object pcAliCreate(String toTopicId,int money,int itype,byte flag){
        int uid = Toolkit.uid();
        // 没登陆也可以打赏
        NutMap nutMap = createPayment(money,itype,toTopicId,flag,uid);
        if (nutMap.getInt("ok") == 0){
            return nutMap;
        }
        PayMent payMent = (PayMent) nutMap.get("payMent");
        if (flag == PayType.al.getIndex()){
            // 支付宝支付
            Map<String, String> sParaTemp = new HashMap<String, String>();
            sParaTemp.put("service", AlipayConfig.service);
            sParaTemp.put("partner", AlipayConfig.partner);
            sParaTemp.put("seller_id", AlipayConfig.seller_id);
            sParaTemp.put("_input_charset", AlipayConfig.input_charset);
            sParaTemp.put("payment_type", AlipayConfig.payment_type);
            sParaTemp.put("notify_url", AlipayConfig.notify_url);
            sParaTemp.put("return_url", AlipayConfig.return_url);
            sParaTemp.put("anti_phishing_key", AlipayConfig.anti_phishing_key);
            sParaTemp.put("exter_invoke_ip", AlipayConfig.exter_invoke_ip);

            String name = "打赏";
//            String aa = "";
            try {
                String code = getEncoding(name);
//                Charset.defaultCharset();
//                String code = Utils.codeString(name);
                log.debug("PayService|pcAliCreate|code="+code);
//                name = new String(name.getBytes("ISO-8859-1"),"UTF-8");
                sParaTemp.put("out_trade_no",new String(payMent.getOut_trade_no().getBytes("ISO-8859-1"),"UTF-8") );
                sParaTemp.put("subject", new String(name.getBytes(code),"UTF-8"));//订单名称
                sParaTemp.put("total_fee", new String("0.01".getBytes("ISO-8859-1"),"UTF-8"));
            }catch (Exception e){
                e.printStackTrace();
            }
            log.debug("PayService|pcAliCreate|sParaTemp="+Json.toJson(sParaTemp));
            String sHtmlText = AlipaySubmit.buildRequest(sParaTemp,"get","确认");
            payMent.setTrade_no("");
            dao.insert(payMent);
            return sHtmlText;
        }else if (flag == PayType.wx.getIndex()){
            // 2017/5/22 微信支付 调用统一下单
            String body = "打赏";
            String ip = IPUtil.getRemoteAddr();
            log.debug("PayService|pcAliCreate|ip="+ip);
            if (ip.equals("0:0:0:0:0:0:0:1")){
                // 测试时会走这里
                ip = "127.0.0.1";
            }
            String random = RandomStringGenerator.getRandomStringByLength(32);
            // conf.get("forum.project.url") 服务器地址
            // conf.get("forum.project.name") 工程名
            String url = conf.get("forum.project.url")+"/"+conf.get("forum.project.name")+"/wx/pay/pc/notify";
            Map map = getWxSign(payMent.getOut_trade_no(),1, "星空联盟打赏",ip,"NATIVE",random,new Date(),url);
//            Map map = getWxSign(payMent.getOut_trade_no(),1,body, ip);
            String result_code = (String)map.get("result_code");
            if (result_code == null || !result_code.equals("SUCCESS")){
                return  ServiceUtil.failed("微信下单失败").setv("map",map);
            }
            String return_code = (String)map.get("return_code");
            if (return_code == null || !return_code.equals("SUCCESS")){
                return  ServiceUtil.failed("微信下单失败").setv("map",map);
            }
            payMent.setTrade_no((String) map.get("prepay_id"));
            dao.insert(payMent);
            log.debug("PayService|pcAliCreate|map="+Json.toJson(map));
            return Json.toJson(map);
        }
        return null;
    }
17 回复

签名接口

private Map getWxSign(String out_trade_no,int money, String description,
                          String addr,String trade_type,String random,Date date,String url) {

        UnifiedOrderRequest unifiedOrderRequest = new UnifiedOrderRequest();
        unifiedOrderRequest.setAppid(conf.get("bbs.wx.pay.appId"));
        unifiedOrderRequest.setMch_id(conf.get("bbs.wx.pay.id"));
        unifiedOrderRequest.setNonce_str(random);
        unifiedOrderRequest.setBody(description);
        unifiedOrderRequest.setOut_trade_no(out_trade_no);
        unifiedOrderRequest.setTotal_fee(money+"");
        unifiedOrderRequest.setSpbill_create_ip(addr);
        unifiedOrderRequest.setNotify_url(url);
        unifiedOrderRequest.setTrade_type(trade_type);
        try {
            // 签名
            String sign = Signature.getSign(unifiedOrderRequest);
            unifiedOrderRequest.setSign(sign);
            String content = WXPay.requestOrderService(unifiedOrderRequest);
            log.debug("PayService|getWxSign|content="+content);
            Map map = PayCommonUtil.doXMLParse(content);
            map.put("time_start", date.getTime() / 1000);
            map.put("time_expire", (date.getTime() + 10 * 60 * 1000) / 1000);
            log.debug("map=" + Json.toJson(map));
            return map;
        }catch (Exception e){
            log.info("PayService|getWxSign|out_trade_no="+out_trade_no);
            e.printStackTrace();
        }
        return null;
    }

需要导入微信服务端的demo

服务端的demo地址

https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1

把源码拷贝到自己的工程中,导入相应的jar包
修改微信的源码

/**
     * MD5编码
     * @param origin 原始字符串
     * @return 经过MD5加密之后的结果
     */
    public static String MD5Encode(String origin) {
        String resultString = null;
        try {
            resultString = origin;
            MessageDigest md = MessageDigest.getInstance("MD5");
            resultString = byteArrayToHexString(md.digest(resultString.getBytes("utf-8")));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return resultString;
    }

原来是这句 resultString = byteArrayToHexString(md.digest(resultString.getBytes()));

申请证书

apiclient_cert.p12
apiclient_cert.pem
apiclient_key.pem
rootca.pem
证书使用说明.txt

Configure中加一个统一下单接口地址

public static String order_pay = "https://api.mch.weixin.qq.com/pay/unifiedorder";

创建service类

public class OrderService extends BaseService{

    public OrderService() throws IllegalAccessException, InstantiationException, ClassNotFoundException {
        super(Configure.order_pay);
    }

    /**
     * 请求统一下单
     * @param unifiedOrderRequest 这个数据对象里面包含了API要求提交的各种数据字段
     * @return API返回的数据
     * @throws Exception
     */
    public String request(UnifiedOrderRequest unifiedOrderRequest) throws Exception {
        String responseString = sendPost(unifiedOrderRequest);
        return responseString;
    }
}

WXPay中添加方法

/**
     * 统一下单接口
     * @param unifiedOrderRequest
     * @return API返回的数据
     * @throws Exception
     */
    public static String requestOrderService(UnifiedOrderRequest unifiedOrderRequest) throws Exception{
        return new OrderService().request(unifiedOrderRequest);
    }

MainSetup的init()方法中添加

/**
		 * 初始化SDK依赖的几个关键配置 (微信支付数据初始化)
		 * @param key 签名算法需要用到的秘钥
		 * @param appID 公众账号ID
		 * @param mchID 商户ID
		 * @param sdbMchID 子商户ID,受理模式必填
		 * @param certLocalPath HTTP证书在服务器中的路径,用来加载证书用
		 * @param certPassword HTTP证书的密码,默认等于MCHID
		 */
		WXPay.initSDKConfiguration(conf.get("bbs.wx.pay.secretKey")/*"秘钥"*/,
				conf.get("bbs.wx.pay.appId"),
				conf.get("bbs.wx.pay.id")/*"商户id"*/,
				null, Utils.class.getResource("/custom/wx/apiclient_cert.p12").getPath(),
				conf.get("bbs.wx.pay.id"));

创建订单类

public class UnifiedOrderRequest{
    private String appid;               //公众账号ID
    private String mch_id;              //商户号
    private String nonce_str;           //随机字符串
    private String sign;                //签名
    private String body;                //商品描述
    private String out_trade_no;        //<span style="white-space:pre">    </span>//商户订单号
    private String total_fee;           //总金额
    private String spbill_create_ip;    //<span style="white-space:pre">    </span>//终端IP
    private String notify_url;          //通知地址
    private String trade_type;          //交易类型

    private String time_start;//订单生成时间
    private String time_expire;//订单失效时间

需要的人自己写getset方法吧

异步通知

public String wxNotify(HttpServletRequest request, HttpServletResponse response){
        // 微信异步通知
        String result;//返回给微信的处理结果
        String inputLine;
        String notityXml = "";
        try {
            request.setCharacterEncoding("UTF-8");
            response.setCharacterEncoding("UTF-8");
            response.setContentType("text/html;charset=UTF-8");
            response.setHeader("Access-Control-Allow-Origin", "*");
            try {
                while ((inputLine = request.getReader().readLine()) != null) {
                    notityXml += inputLine;
                }
                request.getReader().close();
            } catch (Exception e) {
                e.printStackTrace();
                result = setXml("fail","xml获取失败");
            }
            if (StringUtils.isBlank(notityXml)) {
                result = setXml("fail","xml为空");
            }
            Map map = PayCommonUtil.doXMLParse(notityXml);
            log.info("PayService|wxNotify|success|map="+Json.toJson(map));
            String return_code = (String) map.get("return_code");
            if (StringUtils.isBlank(return_code)){
                return setXml("fail","return_code为空");
            }
            if (!return_code.equals("SUCCESS")){
                return setXml("fail","return_code为空");
            }
            Map<String, Object> parameters = new HashMap<>();
            parameters.put("appid", map.get("appid"));
            parameters.put("transaction_id", map.get("transaction_id"));
            parameters.put("nonce_str", map.get("nonce_str"));
            parameters.put("bank_type", map.get("bank_type"));
            parameters.put("openid", map.get("openid"));
            parameters.put("fee_type", map.get("fee_type"));//不是必须
            parameters.put("mch_id", map.get("mch_id"));
            parameters.put("cash_fee", map.get("cash_fee"));
            parameters.put("out_trade_no", map.get("out_trade_no"));
            parameters.put("total_fee", map.get("total_fee"));
            parameters.put("trade_type", map.get("trade_type"));
            parameters.put("time_end", map.get("time_end"));
            parameters.put("is_subscribe", map.get("is_subscribe"));// 不是必须
            parameters.put("result_code", map.get("result_code"));
            parameters.put("return_code", map.get("return_code"));
            String sign = Signature.getSign(parameters);
            log.debug("wxNotify|sign="+sign+"|"+map.get("sign"));
            String oldSign = (String)map.get("sign");
            if (!sign.equals(oldSign)){
                return setXml("fail","签名不一致");
            }
            result = setXml("SUCCESS", "OK");
            String out_trade_no = (String) map.get("out_trade_no");
            PayMent payMent = dao.fetch(PayMent.class,out_trade_no);
            if (payMent == null){
                return setXml("fail","订单不存在");
            }
            if (payMent.isSuccess()){
                // 订单已经完成
                return result;
            }
            payMent.setSuccess(true);
            dao.updateIgnoreNull(payMent);
            // 给用户加钱
            userService.addMoney(payMent.getToUserId(),payMent.getMoney());
            log.debug("PayService|wxNotify|success|userId="+Toolkit.uid()+"payMent="+Json.toJson(payMent));
            return result;
        }catch (Exception e){
            log.info("PayService|wxNotify|failed|userId="+Toolkit.uid()+"|e="+PrintUtil.getTrace(e));
        }
        return null;
    }

PayCommonUtil工具类,在网上找到的

import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import java.io.*;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.*;


public class PayCommonUtil {
    //微信参数配置
    public static String API_KEY="";
    public static String APPID="";
    public static String MCH_ID="";
    //随机字符串生成
    public static String getRandomString(int length) { //length表示生成字符串的长度
        String base = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        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();
    }
    //请求xml组装
    public static String getRequestXml(SortedMap<String,Object> parameters){
        StringBuffer sb = new StringBuffer();
        sb.append("<xml>");
        Set es = parameters.entrySet();
        Iterator it = es.iterator();
        while(it.hasNext()) {
            Map.Entry entry = (Map.Entry)it.next();
            String key = (String)entry.getKey();
            String value = (String)entry.getValue();
            if ("attach".equalsIgnoreCase(key)||"body".equalsIgnoreCase(key)||"sign".equalsIgnoreCase(key)) {
                sb.append("<"+key+">"+"<![CDATA["+value+"]]></"+key+">");
            }else {
                sb.append("<"+key+">"+value+"</"+key+">");
            }
        }
        sb.append("</xml>");
        return sb.toString();
    }
    //生成签名
    public static String createSign(String characterEncoding,SortedMap<String,Object> parameters){
        StringBuffer sb = new StringBuffer();
        Set es = parameters.entrySet();
        Iterator it = es.iterator();
        while(it.hasNext()) {
            Map.Entry entry = (Map.Entry)it.next();
            String k = (String)entry.getKey();
            Object v = entry.getValue();
            if(null != v && !"".equals(v)
                    && !"sign".equals(k) && !"key".equals(k)) {
                sb.append(k + "=" + v + "&");
            }
        }
        sb.append("key=" + API_KEY);
        String sign = null;//MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();// TODO: 2017/5/17
        return sign;
    }
    //请求方法
    public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) {
        try {

            URL url = new URL(requestUrl);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();

            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setUseCaches(false);
            // 设置请求方式(GET/POST)
            conn.setRequestMethod(requestMethod);
            conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");
            // 当outputStr不为null时向输出流写数据
            if (null != outputStr) {
                OutputStream outputStream = conn.getOutputStream();
                // 注意编码格式
                outputStream.write(outputStr.getBytes("UTF-8"));
                outputStream.close();
            }
            // 从输入流读取返回内容
            InputStream inputStream = conn.getInputStream();
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            String str = null;
            StringBuffer buffer = new StringBuffer();
            while ((str = bufferedReader.readLine()) != null) {
                buffer.append(str);
            }
            // 释放资源
            bufferedReader.close();
            inputStreamReader.close();
            inputStream.close();
            inputStream = null;
            conn.disconnect();
            return buffer.toString();
        } catch (ConnectException ce) {
            System.out.println("连接超时:{}"+ ce);
        } catch (Exception e) {
            System.out.println("https请求异常:{}"+ e);
        }
        return null;
    }


    //xml解析
    public static Map doXMLParse(String strxml) throws JDOMException, IOException {
        strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");

        if(null == strxml || "".equals(strxml)) {
            return null;
        }

        Map m = new HashMap();

        InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
        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 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();
    }

}

同步回调,查询订单

public NutMap wxQueryOrder(String out_trade_no){
        // 客户端回调查询结果
        if (StringUtils.isBlank(out_trade_no)){
            return ServiceUtil.failed("订单不存在");
        }
        PayMent payMent = dao.fetch(PayMent.class,out_trade_no);
        if (payMent == null){
            return ServiceUtil.failed("订单不存在");
        }
        try {
            ScanPayQueryReqData scanPayQueryReqData = new ScanPayQueryReqData(payMent.getTrade_no(), out_trade_no);
            String content = WXPay.requestScanPayQueryService(scanPayQueryReqData);
            log.info("PayService|wxQueryOrder|success|userId="+Toolkit.uid()+"|out_trade_no="+out_trade_no+"|content="+content);
            Map map = PayCommonUtil.doXMLParse(content);
            log.debug("PayService|wxQueryOrder|success|map="+Json.toJson(map));
            // TODO: 2017/5/15 更新订单状态 ,给用户加钱
            String return_code = (String)map.get("return_code");
            if (StringUtils.isBlank(return_code)){
                return ServiceUtil.failed("查询订单失败");
            }
            if (!return_code.equals("SUCCESS")){
                return ServiceUtil.failed("查询订单失败");
            }
            String trade_state = (String)map.get("trade_state");
            if (StringUtils.isBlank(trade_state)){
                return ServiceUtil.failed("订单支付失败");
            }
            if (!trade_state.equals("SUCCESS")){
                return ServiceUtil.failed("订单支付失败");
            }
            log.debug("PayService|wxQueryOrder|success|userId="+Toolkit.uid()+"payMent="+Json.toJson(payMent));
            // 改变订单状态
            if (payMent.isSuccess()){
                // 订单已经完成
                return ServiceUtil.success("查询订单成功");
            }
            payMent.setSuccess(true);
            dao.updateIgnoreNull(payMent);
            // 给用户加钱
            userService.addMoney(payMent.getToUserId(),payMent.getMoney());
            log.debug("PayService|wxQueryOrder|success|userId="+Toolkit.uid()+"payMent="+Json.toJson(payMent));
            return ServiceUtil.success("查询订单成功");
        }catch (Exception e){
            log.info("PayService|wxQueryOrder|failed|userId="+Toolkit.uid()+"|out_trade_no="+out_trade_no);
        }
        return ServiceUtil.failed("查询订单失败");
    }

有需要的,可以自行修改成自己需要的形式,调试几次也就差不多通了

所用到的jar包也说一下,最主要也就这几个jar包,在上面那个地址中的demo中都有

<dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>fluent-hc</artifactId>
            <version>4.3.5</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.3.5</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient-cache</artifactId>
            <version>4.3.5</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpcore</artifactId>
            <version>4.3.2</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpmime</artifactId>
            <version>4.3.5</version>
            <scope>provided</scope>
        </dependency>

额, nutzwx里面有WxPay

添加回复
请先登陆
回到顶部