微信支付v3服务端

APP下单 

商户系统先调用该接口在微信支付服务后台生成预支付交易单,返回正确的预支付交易会话标识后再按Native、JSAPI、APP等不同场景生成交易串调起支付。

代码

public static Object createOrder(String desc,Long amount,String orderSn) throws Exception {
        PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream(PayConstants.PRIVATE_KEY.getBytes(StandardCharsets.UTF_8)));

        // 使用定时更新的签名验证器,不需要传入证书
        ScheduledUpdateCertificatesVerifier verifier = new ScheduledUpdateCertificatesVerifier(
                new WechatPay2Credentials(PayConstants.MCH_ID, new PrivateKeySigner(PayConstants.MCH_SERIAL_NO, merchantPrivateKey)),
                PayConstants.API_V3KEY.getBytes(StandardCharsets.UTF_8));
        CloseableHttpClient httpClient = WechatPayHttpClientBuilder.create()
                .withMerchant(PayConstants.MCH_ID, PayConstants.MCH_SERIAL_NO, merchantPrivateKey)
                .withValidator(new WechatPay2Validator(verifier))
                .build();

        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/app");
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type","application/json; charset=utf-8");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectMapper objectMapper = new ObjectMapper();

        ObjectNode rootNode = objectMapper.createObjectNode();
        rootNode.put("mchid", PayConstants.MCH_ID)
                .put("appid", PayConstants.APP_ID)
                .put("notify_url", PayConstants.NOTIFY_URL)
                .put("description", desc)
                .put("out_trade_no", orderSn);
        rootNode.putObject("amount")
                .put("total", amount);

        objectMapper.writeValue(bos, rootNode);

        httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
        CloseableHttpResponse response = httpClient.execute(httpPost);

        String bodyAsString = EntityUtils.toString(response.getEntity());
        JsonNode node = objectMapper.readTree(bodyAsString);
        System.out.println(node.get("prepay_id"));

 官方文档:微信支付-开发者文档


APP调起支付

通过APP下单接口获取到发起支付的必要参数prepay_id,可以按照接口定义中的规则,使用微信支付提供的SDK调起APP支付。

APP调起支付的参数需要按照签名规则进行签名计算:

1、构造签名串

签名串一共有四行,每一行为一个参数。行尾以\n(换行符,ASCII编码值为0x0A)结束,包括最后一行。 如果参数本身以\n结束,也需要附加一个\n

参与签名字段及格式

应用id

时间戳

随机字符串

预支付交易会话ID 

数据举例

wx8888888888888888 

1414561699

5K8264ILTKCH16CQ2502SI8ZNMTM67VS

WX1217752501201407033233368018 

代码

//获取当前时间戳
String timestamp = System.currentTimeMillis() + "";
//随机字符串
String nonce = RandomUtil.randomString(32);
StringBuilder builder = new StringBuilder();
//应用id
builder.append(PayConstants.APP_ID).append("\n");
//时间戳
builder.append(timestamp).append("\n");
//随机字符串
builder.append(nonce).append("\n");

JsonNode node = objectMapper.readTree(bodyAsString);
//预支付交易会话ID
builder.append(node.get("prepay_id")).append("\n");

 2、计算签名值

static String sign(byte[] message) throws SignatureException, NoSuchAlgorithmException, InvalidKeyException{
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(PemUtil.loadPrivateKey(PayConstants.PRIVATE_KEY));
        sign.update(message);
        return Base64.getEncoder().encodeToString(sign.sign());
}
String signature = sign(builder.toString().replace("\"","").getBytes(StandardCharsets.UTF_8));
System.out.println(signature);

返回示例

Map<Object,Object> result = new HashMap<>();
result.put("nonceStr",nonce);
result.put("package",PayConstants.PACKAGE);
result.put("timestamp",timestamp);
result.put("sign",signature);
result.put("prepayId",node.get("prepay_id"));
result.put("mchId",PayConstants.MCH_ID);
logger.info("【请求结束】创建订单,result:{}", JSONObject.toJSONString(result));
return ResponseUtil.ok(result);

创建订单完整代码

/**
     * 创建订单
     * @param desc
     * @param amount
     * @return
     * @throws IOException
     * @throws IllegalBlockSizeException
     */
    public static Object createOrder(String desc,Long amount,String orderSn) throws Exception {
        PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream(PayConstants.PRIVATE_KEY.getBytes(StandardCharsets.UTF_8)));

        // 使用定时更新的签名验证器,不需要传入证书
        ScheduledUpdateCertificatesVerifier verifier = new ScheduledUpdateCertificatesVerifier(
                new WechatPay2Credentials(PayConstants.MCH_ID, new PrivateKeySigner(PayConstants.MCH_SERIAL_NO, merchantPrivateKey)),
                PayConstants.API_V3KEY.getBytes(StandardCharsets.UTF_8));
        CloseableHttpClient httpClient = WechatPayHttpClientBuilder.create()
                .withMerchant(PayConstants.MCH_ID, PayConstants.MCH_SERIAL_NO, merchantPrivateKey)
                .withValidator(new WechatPay2Validator(verifier))
                .build();

        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/app");
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type","application/json; charset=utf-8");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectMapper objectMapper = new ObjectMapper();

        ObjectNode rootNode = objectMapper.createObjectNode();
        rootNode.put("mchid", PayConstants.MCH_ID)
                .put("appid", PayConstants.APP_ID)
                .put("notify_url", PayConstants.NOTIFY_URL)
                .put("description", desc)
                .put("out_trade_no", orderSn);
        rootNode.putObject("amount")
                .put("total", amount);

        objectMapper.writeValue(bos, rootNode);

        httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
        CloseableHttpResponse response = httpClient.execute(httpPost);

        String bodyAsString = EntityUtils.toString(response.getEntity());
        System.out.println(bodyAsString);
        //获取当前时间戳
        String timestamp = System.currentTimeMillis() + "";
        //随机字符串
        String nonce = RandomUtil.randomString(32);
        StringBuilder builder = new StringBuilder();
        //应用id
        builder.append(PayConstants.APP_ID).append("\n");
        //时间戳
        builder.append(timestamp).append("\n");
        //随机字符串
        builder.append(nonce).append("\n");

        JsonNode node = objectMapper.readTree(bodyAsString);
        //预支付交易会话ID
        builder.append(node.get("prepay_id")).append("\n");
        System.out.println(builder.toString().replace("\"",""));
        String signature = sign(builder.toString().replace("\"","").getBytes(StandardCharsets.UTF_8));
        System.out.println(signature);

        Map<Object,Object> result = new HashMap<>();
        result.put("nonceStr",nonce);
        result.put("package",PayConstants.PACKAGE);
        result.put("timestamp",timestamp);
        result.put("sign",signature);
        result.put("prepayId",node.get("prepay_id"));
        result.put("mchId",PayConstants.MCH_ID);
        logger.info("【请求结束】创建订单,result:{}", JSONObject.toJSONString(result));
        return ResponseUtil.ok(result);
    }

 配置类 

package com.qiguliuxing.dts.core.util.wechat;

/**
 * @author Xiong
 * @date 2021/12/6 21:55
 */
public interface PayConstants {
    /**
     * 支付成功回调地址
     */
    String NOTIFY_URL = "";
    /**
     * 商户号
     */
    String MCH_ID = "";
    /**
     * 商户证书序列号
     */
    String MCH_SERIAL_NO = "";
    /**
     * api密钥
     */
    String API_V3KEY = "";
    /**
     * appId
     */
    String APP_ID = "";
    /**
     * 签名固定字符串(微信要求的)
     */
    String PACKAGE = "Sign=WXPay";
    /**
     * 你的商户私钥
     */
    String PRIVATE_KEY = "-----BEGIN PRIVATE KEY-----\n" +
            "" +
            "-----END PRIVATE KEY-----";
}

微信回调 

/**
     * 微信回调接口
     * @param request
     * @return
     */
    public Map<Object,Object> weChatCallback(HttpServletRequest request){
        logger.info("【请求开始】微信回调接口,body:{}",request);
        logger.info("Wechatpay-Timestamp:" + request.getHeader("Wechatpay-Timestamp"));
        logger.info("Wechatpay-Nonce:" + request.getHeader("Wechatpay-Nonce"));
        logger.info("Wechatpay-Signature:" + request.getHeader("Wechatpay-Signature"));
        logger.info("Wechatpay-Serial:" + request.getHeader("Wechatpay-Serial"));
        Map<Object,Object> result = new HashMap<>();
        result.put("code","FAIL");
        try {
            StringBuilder signStr = new StringBuilder();
            signStr.append(request.getHeader("Wechatpay-Timestamp")).append("\n");
            signStr.append(request.getHeader("Wechatpay-Nonce")).append("\n");
            BufferedReader br = request.getReader();
            String str = null;
            StringBuilder builder = new StringBuilder();
            while ((str = br.readLine()) != null){
                builder.append(str);
            }
            signStr.append(builder).append("\n");
            //验证签名
            if (!PayUtil.signVerify(request.getHeader("Wechatpay-Serial"),signStr.toString(),request.getHeader("Wechatpay-Signature"))){
                result.put("message","sign error");
                return result;
            }
            ObjectMapper objectMapper = new ObjectMapper();
            JsonNode node = objectMapper.readTree(builder.toString());
            JsonNode resource = node.get("resource");
            String ciphertext = resource.get("ciphertext").textValue().replace("\"","");
            //解密密文
            String xmlResult = PayUtil.decryptOrder(builder.toString());
            logger.info("处理腾讯支付平台的订单支付:{}", JSONObject.toJSONString(xmlResult));
            //验证订单 验证订单号是否存在
            String orderSn = JacksonUtil.parseString(xmlResult,"out_trade_no");
            String transactionId = JacksonUtil.parseString(xmlResult,"transaction_id");
            Map<String,Object> map = JsonMapUtil.jsonToMap(xmlResult);
            Map<String,Object> payer = (HashMap<String,Object>)map.get("payer");
            String openId = payer.get("openid").toString();
            //验证订单是否存在 改变订单状态
            PanPayOrder order = panPayOrderService.selectByOrderSn(orderSn);
            if (order == null){
                result.put("message","订单不存在");
                return result;
            }
            order.setPayId(transactionId);
            order.setPayTime(new Date());
            order.setOrderStatus(1);
            panPayOrderService.updateByPrimaryKeySelective(order);
            //增加会员到期时长
            PanMember panMember = panMemberService.selectByUser(order.getUserId());
            Date time = panMember.getEndTime();
            Calendar cal = Calendar.getInstance();
            cal.setTime(time);
            switch (order.getTypeId()){
                case 1:{
                    cal.add(Calendar.MONTH, 1);
                    break;
                }
                case 2:{
                    cal.add(Calendar.MONTH, 3);
                    break;
                }
                case 3:{
                    cal.add(Calendar.MONTH, 6);
                    break;
                }
                case 4:{
                    cal.add(Calendar.YEAR, 1);
                    break;
                }
                default:{
                    break;
                }
            }
            panMember.setEndTime(cal.getTime());
            panMemberService.updateByPrimaryKeySelective(panMember);
            result.put("code","SUCCESS");
        }catch (IOException e){
            e.printStackTrace();
        }
        logger.info("【请求结束】微信回调接口,result:{}", JSONObject.toJSONString(result));
        return result;
    }

验证签名 

public static boolean signVerify(String serial,String message,String signature){
        PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream(PayConstants.PRIVATE_KEY.getBytes(StandardCharsets.UTF_8)));

        // 使用定时更新的签名验证器,不需要传入证书
        ScheduledUpdateCertificatesVerifier verifier = new ScheduledUpdateCertificatesVerifier(
                new WechatPay2Credentials(PayConstants.MCH_ID, new PrivateKeySigner(PayConstants.MCH_SERIAL_NO, merchantPrivateKey)),
                PayConstants.API_V3KEY.getBytes(StandardCharsets.UTF_8));
        return verifier.verify(serial, message.getBytes(StandardCharsets.UTF_8), signature);
    }

解密密文

public static String decryptOrder(String body) throws IOException {
        try {
            AesUtil util = new AesUtil(PayConstants.API_V3KEY.getBytes(StandardCharsets.UTF_8));
            ObjectMapper objectMapper = new ObjectMapper();
            JsonNode node = objectMapper.readTree(body);
            JsonNode resource = node.get("resource");
            String ciphertext = resource.get("ciphertext").textValue();
            String associatedData = resource.get("associated_data").textValue();
            String nonce = resource.get("nonce").textValue();
            return util.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8),nonce.getBytes(StandardCharsets.UTF_8),ciphertext);
        } catch (JsonProcessingException | GeneralSecurityException e) {
            e.printStackTrace();
        }
        return null;
    }


版权声明:本文为weixin_51168459原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。