java整合微信支付功能详细示例

 更新时间:2023年07月08日 11:32:43   作者:abilities  
这篇文章主要给大家介绍了关于java整合微信支付功能的相关资料,支付是一个复杂且测试起来需要的配置特别复杂的模块,文中给出了详细的示例代码,需要的朋友可以参考下

微信服务商户号和普通商户号的区别

1. 适用范围不同:

微信服务商户号适用于代理商、第三方服务商等,也就是说,只有中间商才可以成功创建微信服务商户号。而普通商户号适用于普通商家。

2. 支付方式不同:

微信服务商户号支持多个子商户从属于一个服务商,实现统一线上收款和资金结算,同时支持分账等多种支付方式,方便代理商为子商户提供中间担保结算、派单抽成、资金归集、单一入口、风控应用等服务。

普通商户号只能被单独占有,支持一种支付方式,即从客户直接收款,对于多个门店的商家,需要分别开通每个门店的商户号,管理起来比较繁琐。

3. 申请及审核方式不同:

申请微信服务商户号需要提供相关商业资质证明,如公司注册证明、公司经营资质证明等,审核通过后方可获得认证。

普通商户号的申请相对较为简单,只需要提供公司营业执照、结算银行账号等资料,审核通过后即可开通商户号。

总之,微信服务商户号适合代理商、第三方服务商等机构场景,可以实现代理收款、分账结算等多种支付服务,方便代理商为子商户提供完整的商业解决方案;而普通商户号适合单个商家接受在线收款,管理更为简单。

使用

这里我们就先配置服务商号

微信商户号中配置信息

  • 需要提供企业相关的资料进行等待审核大概在一周左右
  • 开通商户号
  • 创建子商户号
  • 生成API3私钥和密钥
  • 授权支付回调目录
  • 在微信开发者平台中小程序关联服务商户号

代码

Jar依赖

<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-csv</artifactId>
    <version>2.11.2</version>
</dependency>
<dependency>
    <groupId>com.github.wechatpay-apiv3</groupId>
    <artifactId>wechatpay-apache-httpclient</artifactId>
    <version>0.2.2</version>
</dependency>

配置微信API

#微信配置
wx-applet:
  url: https://api.mch.weixin.qq.com/papay/querycontract
  payUrl: https://api.mch.weixin.qq.com/pay/pappayapply
  notifyUrl: https://chillman.com.cn/applet/applyPayNotify
  qureyOrderUrl: https://chillman.com.cn/pay/paporderquery
  appId: 小程序应用ID
  appSecret: 小程序密钥
  mchId: 子商户id
#微信支付配置
wechat-pay:
  url: https://api.mch.weixin.qq.com
  appId: 小程序Id
  mchId: 商户号
  mchSerialNo: 
  apiV3Key: 私钥key
  #  商户key
  mchPrivateKey: 私钥
  notifyUrl: 回调地址
  apiClientKey: 私钥
  certificatesUrl: https://api.mch.weixin.qq.com/v3/certificates

微信API工具类

package com.snow.param;
import lombok.Data;
/**
 * @Author: zhaohaoxin
 * @Date: 2023-06-02-16:10
 */
@Data
public class PlaceAnOrderParam2 {
    private  String sp_appid;
    private  String sp_mchid;
    private  String sub_appid;
    private  String sub_mchid;
    private  String description;
    private  String out_trade_no;
    private  String notify_url;
    private AmountParam amount;
    private PayerParam payer;
    public PlaceAnOrderParam2(String sp_appid, String sp_mchid, String sub_appid, String sub_mchid, String description, String out_trade_no, String notify_url, AmountParam amount, PayerParam payer) {
        this.sp_appid = sp_appid;
        this.sp_mchid = sp_mchid;
        this.sub_appid = sub_appid;
        this.sub_mchid = sub_mchid;
        this.description = description;
        this.out_trade_no = out_trade_no;
        this.notify_url = notify_url;
        this.amount = amount;
        this.payer = payer;
    }
}
package com.snow.service.imple;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.snow.api.WXPayRequest;
import com.snow.constant.PathConstant;
import com.snow.dto.WXPayDto;
import com.snow.param.OrderStatusParam;
import com.snow.param.PlaceAnOrderParam;
import com.snow.param.PlaceAnOrderParam2;
import com.snow.param.SingParam;
import com.snow.service.WXPayService;
import com.snow.sgin.NewPaySignUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
@Slf4j
public class WXPayServiceImpl implements WXPayService {
    @Autowired
    private WXPayRequest wxPayRequest;
    @Autowired
    private NewPaySignUtil newPaySignUtil;
    public static final String SING_TYPE = "RSA_1_256";
    public static final String PATH_JSAPI = "/v3/pay/partner/transactions/jsapi";
    @Override
    public String WXPayUnifyOrder(PlaceAnOrderParam placeAnOrderParam) {
        try {
            String reqBody = "";
            if (placeAnOrderParam != null) {
                ObjectMapper objectMapper = new ObjectMapper();
                reqBody = objectMapper.writeValueAsString(placeAnOrderParam);
            }
            return wxPayRequest.requestOnce(PATH_JSAPI, reqBody);
        }catch (Exception e){
            log.error("调用微信支付统一下单失败",e);
            return StringUtils.EMPTY;
        }
    }
    @Override
    public String WXPayUnifyOrder(PlaceAnOrderParam2 placeAnOrderParam) {
        try {
            String reqBody = "";
            if (placeAnOrderParam != null) {
                ObjectMapper objectMapper = new ObjectMapper();
                reqBody = objectMapper.writeValueAsString(placeAnOrderParam);
            }
            return wxPayRequest.requestOnce(PathConstant.PATH_JSAPI, reqBody);
        }catch (Exception e){
            log.error("调用微信支付统一下单失败",e);
            return StringUtils.EMPTY;
        }
    }
    @Override
    public String getOrderStatus(OrderStatusParam orderStatusParam) {
        try {
            String reqBody = "";
            String url = PathConstant.PATH_GET_ORDER + orderStatusParam.getOutTradeNo() + "?mchid="+orderStatusParam.getMchid();
            return wxPayRequest.requestOnce(url,reqBody);
        }catch (Exception e){
            log.error("调用查询订单状态接口失败",e);
            return StringUtils.EMPTY;
        }
    }
    @Override
    public WXPayDto getWXPaySing(SingParam singParam) {
        String appid = singParam.getAppid();
        long timeStamp = System.currentTimeMillis() / 1000;
        String nonceStr = RandomStringUtils.randomAlphanumeric(32);
        String prepayId = "prepay_id=" + singParam.getPrepayId();
        String preStr = appid+"\n"
                +timeStamp+"\n"
                +nonceStr+"\n"
                +prepayId+"\n";
        String paySign = newPaySignUtil.getSign(SING_TYPE, preStr);
        WXPayDto wxPayDto = new WXPayDto(timeStamp,prepayId,nonceStr,paySign);
        return wxPayDto;
    }
}

发起支付后获取微信小程序根据prepayId参数调用支付方法弹出支付会话框

微信回调

package com.snow.controller;
import com.alibaba.fastjson.JSON;
import com.snow.annotation.SystemLog;
import com.snow.business.domain.XqSnowOrder;
import com.snow.business.domain.XqSnowOrderExample;
import com.snow.business.service.XqSnowOrderService;
import com.snow.common.exception.RequestWechatException;
import com.snow.common.utils.DateUtils;
import com.snow.enumz.OrderStateEnum;
import com.snow.pem.PayResponseUtils;
import com.snow.pem.StaticParameter;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.util.Base64Utils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.security.GeneralSecurityException;
import java.security.Signature;
import java.security.cert.X509Certificate;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Controller
@RestController
@RequestMapping("/wxPay/callBack")
@Slf4j
public class WXPayCallBackController {
    @Value("${wechat-pay.apiV3Key}")
    private String apiV3Key;
    @Autowired
    private XqSnowOrderService orderService;
    @SystemLog(menu = "微信支付支付结果通知回调")
    @PostMapping("wxPayRequest")
    public String wxPayRequest(HttpServletRequest request, HttpServletResponse response){
        log.info("==微信回调开始==");
        Map<String, String> map = new HashMap<>();
        try {
            InputStream in = request.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(in, "UTF-8"));
            StringBuffer param = new StringBuffer();
            String line;
            while ((line = br.readLine()) != null) {
                param.append(line);
            }
            String callBackParam = param.toString();
            JSONObject object = JSONObject.fromObject(callBackParam);
            log.info("微信回调参数={}" + callBackParam);
            //回调摘要
            String summary = object.getString("summary");
            //如果验证签名序列号通过
            if (verifiedSign(request,callBackParam)){
                JSONObject resource = object.getJSONObject("resource");
                String associatedData = resource.getString("associated_data");
                String nonce = resource.getString("nonce");
                String ciphertext = resource.getString("ciphertext");
                AesUtil aesUtil = new AesUtil(apiV3Key.getBytes());
                String res = aesUtil.decryptToString(associatedData.getBytes(), nonce.getBytes(), ciphertext);
                JSONObject resObj = JSONObject.fromObject(res);
                log.info("微信通知支付成功通知参数={}",resObj);
                //交易状态
                String tradeState = resObj.getString("trade_state");
                //订单号
                String outTradeNo = resObj.getString("out_trade_no");
                //支付完成时间
                String time_end = resObj.getString("success_time");
                //微信交易订单号
                String transactionId = resObj.getString("transaction_id");
                //订单金额
                com.alibaba.fastjson.JSONObject amount = JSON.parseObject(resObj.getString("amount"));
                //总金额
                String total = amount.getString("total");
                //用户支付金额
                String payerTotal = amount.getString("payer_total");
                OrderStateEnum orderStateEnum = OrderStateEnum.OK;
                if (tradeState.equals("SUCCESS")){
                    //查询订单状态
                    XqSnowOrderExample snowOrderExample = new XqSnowOrderExample();
                    snowOrderExample.createCriteria().andorderNoEqualTo(outTradeNo);
                    XqSnowOrder snowOrder = orderService.selectFirstByExample(snowOrderExample);
                    //订单已经处理
                    if(snowOrder.getPayStatus().equals(orderStateEnum.getState()) ){
                        log.info("扣款失败订单已经处理,订单号:"+outTradeNo+",订单状态:"+snowOrder.getPayStatus());
                        map.put("code", "SUCCESS");
                        map.put("message", "成功");
                        return JSON.toJSONString(map);
                    }
                    Date timeEnd = null;
                    try {
                        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
                        timeEnd = sdf.parse(time_end);
                    } catch (ParseException e) {
                        e.printStackTrace();
                    }
                    BigDecimal cashFee = new BigDecimal(payerTotal).divide(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_DOWN);
                    XqSnowOrder snowOrderEdition = new XqSnowOrder();
                    //微信支付交易号
                    snowOrderEdition.setWxOrderNo(transactionId);
                    //修改订单状态为已支付
                    snowOrderEdition.setPayStatus(orderStateEnum.getState());
                    snowOrderEdition.setPayTime(timeEnd);
                    if (Integer.valueOf(total)-Integer.valueOf(payerTotal)>0){
                        //有优惠
                        //代金券或立减优惠金额
                        //优惠功能
                        if(resObj.containsKey("promotion_detail")){
                            JSONArray promotionDetails = JSONArray.fromObject(resObj.getString("promotion_detail"));
                            if(promotionDetails != null){
                                JSONObject promotionDetail = promotionDetails.getJSONObject(0);
                                String coupon_fee = promotionDetail.getString("amount");
                                if (!StringUtils.isEmpty(coupon_fee)){
                                    BigDecimal couponFee = new BigDecimal(coupon_fee).divide(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_DOWN);
                                    //tMoheOrder.setWxCouponFee(couponFee);
                                    log.info("订单号:{},有优惠-->couponFee:,{}",outTradeNo,coupon_fee);
                                }
                                //代金券或立减优惠ID
                                String coupon_id_$n = promotionDetail.getString("coupon_id");
                                if (!StringUtils.isEmpty(coupon_id_$n)){
                                    //tMoheOrder.setWxCouponId(coupon_id_$n);
                                    log.info("订单号:{},,代金券或立减优惠ID:{}",outTradeNo,coupon_id_$n);
                                }
                            }
                        }
                        log.info("此订单有优惠,订单号:"+outTradeNo);
                    }
                    //没有优惠
                    XqSnowOrderExample snowOrderExampleEdit = new XqSnowOrderExample();
                    snowOrderExampleEdit.createCriteria().andorderNoEqualTo(outTradeNo);
                    orderService.updateByExampleSelective(snowOrderEdition,snowOrderExampleEdit);
                    log.info("扣款订单微信支付处理成功,此订单已经支付,订单号码:"+outTradeNo);
                }else{
                    log.info("微信返回支付错误摘要:"+summary);
                }
                map.put("code", "SUCCESS");
                map.put("message", "成功");
            }else{
                log.info("扣款回调签名验证失败");
            }
        } catch (IOException e) {
            log.error("微信支付支付结果通知回调接口异常:",e);
        } catch (ParseException e) {
            log.error("微信支付支付结果通知回调接口异常:",e);
        } catch (RequestWechatException e) {
            log.error("微信支付支付结果通知回调接口异常:",e);
        } catch (GeneralSecurityException e) {
            log.error("微信支付支付结果通知回调接口异常:",e);
        }
        return JSON.toJSONString(map);
    }
    /**
     * 验证微信签名
     * @param request
     * @param body
     * @return
     * @throws GeneralSecurityException
     * @throws IOException
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws ParseException
     */
    private boolean verifiedSign(HttpServletRequest request,String body) throws GeneralSecurityException, ParseException, RequestWechatException {
        //微信返回的证书序列号
        String serialNo = request.getHeader("Wechatpay-Serial");
        //微信返回的随机字符串
        String nonceStr = request.getHeader("Wechatpay-Nonce");
        //微信返回的时间戳
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        //微信返回的签名
        String wechatSign = request.getHeader("Wechatpay-Signature");
        //组装签名字符串
        String signStr = Stream.of(timestamp, nonceStr, body)
                .collect(Collectors.joining("\n", "", "\n"));
        //当证书容器为空 或者 响应提供的证书序列号不在容器中时  就应该刷新了
        if (StaticParameter.certificateMap.isEmpty() || !StaticParameter.certificateMap.containsKey(serialNo)) {
            PayResponseUtils.refreshCertificate();
        }
        //根据序列号获取平台证书
        X509Certificate certificate = StaticParameter.certificateMap.get(serialNo);
        //获取失败 验证失败
        if (certificate == null){
            return false;
        }
        //SHA256withRSA签名
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initVerify(certificate);
        signature.update(signStr.getBytes());
        //返回验签结果
        return signature.verify(Base64Utils.decodeFromString(wechatSign));
    }
}

效果

以上就是小程序支付的整个流程

总结

到此这篇关于java整合微信支付功能的文章就介绍到这了,更多相关java整合微信支付内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

最新评论