微信小程序支付(java后端)

本文档详细介绍了如何使用Java、SpringCloud和微信支付API实现微信小程序支付功能。包括统一下单接口、预支付参数设置、签名生成等关键步骤,以及相关工具类的使用。在开发过程中需要注意参数的正确性和签名的生成,确保支付流程的正常进行。
摘要由CSDN通过智能技术生成

微信支付文档传送门:https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_3

1.开发工具:
idea+springcloud+微信web开发工具

2.开发环境:
java+maven

3.开发前准备:
3.1 所需材料
小程序的appid,APPsecret,支付商户号(mch_id),商户密钥(key),付款用户的openid。

申请接入微信商户地址:https://pay.weixin.qq.com/static/applyment_guide/applyment_detail_miniapp.shtml

3.2 开发模式
本次开发采用的开发模式是:普通模式,适用于有自己开发团队或外包开发商的直连商户收款。开发者申请自己的appid和mch_id,两者需具备绑定关系,以此来使用微信支付提供的开放接口,对用户提供服务。

开发 java

一、控制层 PaymentController.java

/**
 * 微信预支付Api
 * 统一下单接口
 * @param openId
 * @return
 */
@ApiOperation("微信预支付1")
@ApiImplicitParams({
        @ApiImplicitParam(name = "openId",value = "用户openId")
})
@PostMapping(value = {"jsapipays"})
public ResponseEntity jsapipays(@RequestParam String openId) {
    Condition usercondition = new Condition(User.class);
    usercondition.createCriteria().andEqualTo("wxId",openId);
    User user=userService.findByCondition(usercondition,null).get(0);
    //通过用户ID查询蓝牙订单
    Condition bizBluetoothOrderC=new Condition(BizBluetoothOrder.class);
    bizBluetoothOrderC.createCriteria().andEqualTo("uId",user.getId());
    List<BizBluetoothOrder> bizBluetoothOrderlist = bizBluetoothOrderService.findByCondition(bizBluetoothOrderC,null);
    if(bizBluetoothOrderlist.isEmpty()){
        return failResult("订单不存在!");
    }
    BizBluetoothOrder bizBluetoothOrder = bizBluetoothOrderlist.get(0);
    Map jsapi = wxPayService.jsapi2(bizBluetoothOrder.getOrderSn(),"18.0" ,openId);
    return successResult(jsapi);
}

二、service层

/**
 * 微信小程序支付
 * @param orderNum
 * @param openId
 * @return
 */
Map jsapi2(String orderNum , String total_fee, String openId);

三、实现类

/**
 * 统一下单开始 方法1
 * @param orderNum
 * @param total_fee
 * @param openId
 * @return
 */
@Override
public Map jsapi2(String orderNum , String total_fee, String openId) {
    try {
        log.info("订单号=="+orderNum);
        //拼接统一下单地址参数
        SortedMap<String,String> paraMap = new TreeMap();
        paraMap.put("appid", WXConstEnum.appId);
        paraMap.put("body", "支付订单");
        paraMap.put("mch_id", WXConstEnum.mch_id);
        paraMap.put("nonce_str", WxPayUtils.makeUUID(32).toUpperCase());
        paraMap.put("signType",WXConstEnum.SIGNTYPE);
        paraMap.put("openid", openId);
        paraMap.put("out_trade_no",orderNum);//订单号  商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*且在同一个商户号下唯一
        paraMap.put("spbill_create_ip", WxPayUtils.getLocalIp());
        paraMap.put("total_fee", total_fee);
        paraMap.put("timeStamp", WxPayUtils.getCurrentTimeStamp());
        paraMap.put("trade_type",WXConstEnum.TRADETYPE);
        paraMap.put("notify_url",WXConstEnum.notify_url);// 此路径是微信服务器调用支付结果通知路径随意写
        String sign =  WXPayUtil.generateSignature(paraMap, WXConstEnum.key).toUpperCase();
        paraMap.put("sign", sign);
        String xml = WxPayUtils.mapToXml(paraMap);//将所有参数(map)转xml格式
        log.info("xml源串 = " + xml);
        // 统一下单地址 https://api.mch.weixin.qq.com/pay/unifiedorder
        String xmlStr = HttpUtils.sendPost(WXConstEnum.pay_url, xml);//发送post请求"统一下单接口"返回预支付id:prepay_id
        log.info("返回xmlStr = " + xmlStr);
        //以下内容是返回前端页面的json数据
        String prepay_id = "";//预支付id
        if (xmlStr.indexOf("SUCCESS") != -1) {
            Map<String, String> map = WxPayUtils.xmlToMap(xmlStr);
            prepay_id = map.get("prepay_id");
        }
        Map<String, String> payMap = new HashMap<String, String>();
        payMap.put("appId", WXConstEnum.appId);
        payMap.put("timeStamp", WxPayUtils.getCurrentTimeStamp());
        payMap.put("nonceStr", WxPayUtils.makeUUID(32));
        payMap.put("signType", "MD5");
        String paySign =  WXPayUtil.generateSignature(paraMap, WXConstEnum.key).toUpperCase();
        payMap.put("paySign", paySign);
        payMap.put("package", "prepay_id=" + prepay_id);
        return payMap;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}四、调用统一下单的接口 参数类WXConstEnum.java
/**
 * TODO 替换成自己的参数
 */
//微信小程序appid
public static String appId = "wx34118d54080e5555";
//微信商户号
public static String mch_id="1612911113";
//微信支付的商户密钥
public static final String key = "50c01580a2824d48a48d94e9f6b046fb";
//支付成功后的服务器回调url
public static final String notify_url="http://shopdev.lyproduct.cn/lyshop-app/app/orderapi/miniNotify";
//签名方式
public static final String SIGNTYPE = "MD5";
//交易类型
public static final String TRADETYPE = "JSAPI";
//微信统一下单接口地址
public static final String pay_url = "https://api.mch.weixin.qq.com/pay/unifiedorder";

//微信支付返回状态码
public static final String SUCCESS = "SUCCESS";

五、WxPayUtils.java 需要用到的工具类 官方文档会有提供SDK

/**
 * XML格式字符串转换为Map
 *
 * @param strXML XML字符串
 * @return XML数据转换后的Map
 * @throws 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) {
        throw ex;
    }

}

/**
 * 将Map转换为XML格式的字符串
 *
 * @param data Map类型数据
 * @return XML格式的字符串
 * @throws Exception
 */
public static String mapToXml(SortedMap<String, String> data) throws Exception {
    DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
    //防止XXE攻击
    documentBuilderFactory.setXIncludeAware(false);
    documentBuilderFactory.setExpandEntityReferences(false);
    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();
    try {
        writer.close();
    }
    catch (Exception ex) {
    }
    return output;
}


/**
 * 生成签名
 *
 * @param data 待签名数据
 * @param key API密钥
 * @return 签名
 */
public static String generateSignature(final Map<String, String> data, String key) throws Exception {
    return generateSignature(data, key, WXPayConstants.SignType.MD5);
}

/**
 * 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。
 *
 * @param data 待签名数据
 * @param key API密钥
 * @param signType 签名方式
 * @return 签名
 */
public static String generateSignature(final Map<String, String> data, String key, WXPayConstants.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 (WXPayConstants.SignType.MD5.equals(signType)) {
        return MD5(sb.toString()).toUpperCase();
    }
    else if (WXPayConstants.SignType.HMACSHA256.equals(signType)) {
        return HMACSHA256(sb.toString(), key);
    }
    else {
        throw new Exception(String.format("Invalid sign_type: %s", signType));
    }
}
/**
 * 生成 MD5
 *
 * @param data 待处理数据
 * @return MD5结果
 */
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 data 待处理数据
 * @param key 密钥
 * @return 加密结果
 * @throws Exception
 */
public static String HMACSHA256(String data, String key) throws Exception {
    Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
    SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
    sha256_HMAC.init(secret_key);
    byte[] array = sha256_HMAC.doFinal(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();
}





/**
 * 获取有序map
 * @param map
 * @return
 */
public static SortedMap<String,String> getSortedMap(Map<String,String> map){

    SortedMap<String, String> sortedMap = new TreeMap<>();
    Iterator<String> it =  map.keySet().iterator();
    while (it.hasNext()){
        String key  = (String)it.next();
        String value = map.get(key);
        String temp = "";
        if( null != value){
            temp = value.trim();
        }
        sortedMap.put(key,temp);
    }
    return sortedMap;
}

/**
 * 生成随机数
 *
 * @return
 */
public static String makeUUID(int len) {
    return UUID.randomUUID().toString().replaceAll("-", "").substring(0, len);
}

/**
 * 获取当前的Timestamp
 *
 * @return
 */
public static String getCurrentTimeStamp() {
    return Long.toString(System.currentTimeMillis()/1000);
}

/**
 * 获取当前的时间
 * @return
 */
public static long getCurrentTimestampMs() {
    return System.currentTimeMillis();
}
/**
 * 获取当前机器的ip
 *
 * @return String
 */
public static String getLocalIp(){
    InetAddress ia=null;
    String localip = null;
    try {
        ia=ia.getLocalHost();
        localip=ia.getHostAddress();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return localip;

}

/**
 * 转换金额型到整型
 * @param money
 * @return
 */
public static String moneyToIntegerStr(Double money){
    BigDecimal decimal = new BigDecimal(money);
    int amount = decimal.multiply(new BigDecimal(100))
            .setScale(0, BigDecimal.ROUND_HALF_UP).intValue();
    return String.valueOf(amount);
}

/**
 * 生成订单号
 *
 * @return
 */
public static String generateOrderNo() {
    SimpleDateFormat sdf  = new SimpleDateFormat("yyMMdd");
    return sdf.format(new Date())+makeUUID(16);
}

/**
 * 获取当前工程url
 *
 * @param request
 * @return
 */
public static String getCurrentUrl(HttpServletRequest request){
    return request.getScheme() +"://" + request.getServerName()  + ":" +request.getServerPort() +request.getContextPath();
}

/**
 * 转换double 为 int string
 * @param payAmount
 * @return
 */
public static String moneyToIntegerStr(double payAmount){
    String money = String.valueOf(payAmount);
    return money;
}

传递的xml参数必须按照官方接口文档(https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_1),如果出现sing签名错误的问题,试试修改商户密钥(本人修改了三次才成功)。

control层调用接口时 我是值传递了openId 可以根据下单的需要去其他参数(注意大小写)

所有的参数都有,应该不会看不懂吧。直接复制粘贴!

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
微信小程序商城Java后端源码是一种为了保证微信小程序的正常运行而编写的程序源码。Java后端程序主要负责处理前端小程序中的数据,通过编写后端代码,使得小程序用户能够正常使用小程序,并实现小程序商城的各种功能。 在微信小程序商城Java后端源码中,常见的功能包括但不限于: 用户登录注册,商品展示,购物车管理,订单系统等。通过Java后端程序源码的编写,实现这些功能的同时,还保证了小程序商城的稳定性和安全性。 首先,对于小程序商城的用户登录和注册,Java后端程序需要通过数据验证和权限验证等流程来保证用户信息的安全性。在Java后端程序中,可以通过编写用户密码加密以及身份鉴别等相关功能代码来保证用户信息的安全性。 其次,对于商品展示,Java后端程序可以通过数据库的建立和商品信息的存储来方便进行商品展示。用户可以在小程序端查看商品信息并进行购买操作,Java后端可在用户购买商品后进行库存管理、定价管理等相关操作。 此外,在购物车管理和订单系统方面,Java后端程序负责对购物车和订单相关信息的管理。Java后端程序可以通过与微信支付API的整合,来编写实现小程序商城中的支付功能,确保了用户购物的便利性和安全性。 综上所述,微信小程序商城Java后端源码的意义在于它能够对于小程序商城进行完整的控制和操作。通过编写Java后端程序源码,可以保证用户信息的安全性和商城运营的高效性。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值