【微信小程序】Java实现微信支付(小程序支付JSAPI-V3)java-sdk工具包

这篇具有很好参考价值的文章主要介绍了【微信小程序】Java实现微信支付(小程序支付JSAPI-V3)java-sdk工具包。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

      对于一个没有写过支付的小白,打开微信支付官方文档时彻底懵逼,因为微信支付文档太过详细,导致我无从下手,所以写此文章,帮助第一次写支付的小伙伴梳理一下。

一、流程分为三个接口:(这是前言,先看一遍,保持印象,方便理解代码)

1、第一个接口:微信预支付 小程序调用后端预支付接口  =>预支付接口调用成功返回给小程序支付凭证id
(如下返回示例示例,能返回这些代表后端工作完成了一半了,在第一个接口中,我加入了保存订单信息业务,将订单状态保存为待支付状态)

{
    "timeStamp": "1414561699",
    "nonceStr": "5K8264ILTKCH16CQ2502SI8ZNMTM67VS",
    "package": "prepay_id=wx201410272009395522657a690389285100",
    "paySign": "oR9d8PuhnIc+YZ8cBHFCwfgpaK9gd7vaRvkYD7rthRAZ\/X+QBhcCYL21N7cHCTUxbQ+EAt6Uy+lwSN22f5YZvI45MLko8Pfso0jm46v5hqcVwrk6uddqNjKNqZLhLw4jq\/xDg=="
  }

        这些就是第一个接口需要返回的数据,也是微信支付官方文档要求返回的数据,有了这些支付凭证,前端就可以拿去直接调用微信后台支付接口。

2、第二个接口:支付回调 小程序支付成功后,微信后台执行支付回调将支付订单信息返回。(在此接口中,我将上一个接口的待支付状态更新为订单已支付状态,并且保存充值流水日志,更新充值人的余额操作。) 

此接口可能出现的问题:①.在正常流程下,已经完成了支付,但是如果某些原因导致支付回调失败,比如由于微信的原因导致多次调用了此接口,针对此接口幂等性,我们可以在做业务更新流程之前先进行订单的状态判断,让已经回调一次的订单不再进行保存流水以及余额的更新。

②.由于服务器宕机或者延迟等问题原因回调接口失败时,这时用户有可能已经付钱,但是没有进行业务更新流程,导致充值没有余额增加,充值流水缺失等问题,就要需要第三个接口解决。

3.第三个接口:支付订单查询 此订单查询顾名思义就是查询订单,先去查询订单状态,如果为已支付说明回调没问题,如果为待支付,就去微信提供的接口查询是否支付,支付成功就重复第二步中更新订单状态保存订单流水更新用户余额操作,因为第二步回调失败所以需要再次此操作。

后续更新加锁操作,进行并发控制。。。。。。

二、开始撸代码

1、maven
<dependency>
  <groupId>com.github.wechatpay-apiv3</groupId>
  <artifactId>wechatpay-java</artifactId>
  <version>0.2.11</version>
</dependency>
2、资源文件配置:配置微信支付前必要的密钥和商户信息

微信支付 apiv3 java,微信小程序,微信,小程序,java,spring cloud,spring boot

# 微信小程序支付配置信息
wx:
  # 微信小程序appid
  app-id: *********
  # 商户号
  mch-id: **********
  # 证书序列号
  mch-serial-no: **********
  # 小程序密钥
  app-secret: **********
  # api密钥
  api-key: ***********
  # 回调接口地址
  notify-url: https://*********/payNotify
  # 证书地址
  key-path: /data/iot/cert/apiclient_key.pem(这是我服务器中apiclient_key.pem文件存放地址,测试改成本地的)
3、获取配置信息
@Component
@ConfigurationProperties(prefix = "wx")
@Data
@ToString
public class WxPayV3Bean {
    //小程序appid
    private String appId;
    //商户号
    private String mchId;
    //证书序列号
    private String mchSerialNo;
    //小程序秘钥
    private String appSecret;
    //api秘钥
    private String apiKey;
    //回调接口地址
    private String notifyUrl;
    //证书地址
    private String keyPath;
}
4、预支付请求类(由前端传值 根据自己业务请求添加)
@Data
@Accessors(chain = true)
public class WxPayOrderReqVO {

    @ApiModelProperty(value = "订单支付类型(商品订单;预约订单)",required = true)
    @NotBlank(message = "订单支付类型不能为空!")
    private String orderType;//附加数据,回调时可根据这个数据辨别订单类型或其他
 
    @ApiModelProperty(value = "总金额(单位:分)",required = true)
    @NotNull(message = "总金额不能为空!")
    private BigDecimal amount;

    @ApiModelProperty(value = "商品描述",required = true)
    @NotBlank(message = "商品描述不能为空!")
    private String description;
5、工具类
import com.wechat.pay.java.core.util.PemUtil;
import org.springframework.util.Base64Utils;

import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Random;
 
/**
 * @project
 * @Classname WXPayUtil
 * @Description TODO
 * @Author: lsh
 * @CreateTime: 2023-10-24  16:00
 */
public class WXPayUtil {
 
    public static String getSign(String signatureStr,String privateKey) throws InvalidKeyException, NoSuchAlgorithmException, SignatureException, IOException, URISyntaxException {
        //replace 根据实际情况,不一定都需要
        String replace = privateKey.replace("\\n", "\n");
        PrivateKey merchantPrivateKey = PemUtil.loadPrivateKeyFromPath(replace);
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(merchantPrivateKey);
        sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
        return Base64Utils.encodeToString(sign.sign());
    }
 
}
6、开始写支付接口

conteoller层 (包含预支付接口 支付回调接口,像支付保存订单业务可以写在与预付接口里

ApiResponseBody 为我封装的统一返回接口,改成自己的!!!

/**
 * @author lsh
 * @ClassName WxPayController
 * @description: TODO
 * @date 2023年10月24日
 */
@Slf4j
@Api(value = "微信-支付", tags = {"微信-支付"})
@RestController
@RequestMapping("wechat/pay")
public class WxPayController{
 
    @Resource
    private WxPayService wxPayService;

    /**
     * 微信预支付
     * @param req
     * @param request
     * @return
     * @throws Exception
     */
    @ApiOperation(value = "微信预支付", notes = "微信预支付")
    @PostMapping("/createOrder")
    public ApiResponseBody createOrder(@RequestBody @Validated WxPayOrderReqVO req, HttpServletRequest request) throws Exception {
        return wxPayService.createOrder(req, request);
    }

    /**
     * 微信支付回调
     * @param request
     * @return
     * @throws Exception
     */
    @ApiOperation(value = "微信支付回调", notes = "微信支付回调")
    @PostMapping("/payNotify")
    public ApiResponseBody payNotify(HttpServletRequest request) throws Exception {
        log.info("-----------------------微信支付回调通知-----------------------");
       //注意:回调接口需要暴露到公网上,且要放开token验证
        return  wxPayService.payNotify(request);
    }

/**
     * 查询支付
     * @param
     * @return
     * @throws Exception
     */
    @ApiOperation(value = "查询支付", notes = "查询支付")
    @GetMapping("/queryPayOrder")
    public ApiResponseBody queryPayOrder(@RequestParam("tradeNo") String tradeNo) {
        log.info("-----------------------订单号:" + tradeNo);
        if (tradeNo == null || "".equals(tradeNo)) {
            return ApiResponseBody.error(BizCodeMsgEnum.PARAM_ERROR);
        }
        return  wxPayService.queryPayOrder(tradeNo);
    }
}

Service、ServiceImpl层:

    ApiResponseBody createOrder(WxPayOrderReqVO req, HttpServletRequest request) throws Exception;

    ApiResponseBody payNotify(HttpServletRequest request) throws Exception;

    ApiResponseBody queryPayOrder(String tradeNo);

/**
 * @author lsh
 * @ClassName WxPayServiceImpl
 * @description: TODO
 * @date 2023年10月24日
 */
@Service
@Slf4j
public class WxPayServiceImpl implements WxPayService {

    @Resource
    private OrderMapper orderMapper;
    @Resource
    private WxPayV3Bean wxPayV3Bean;
    @Resource
    private WechatUserMapper wechatUserMapper;
    @Resource
    private AccountUserMapper accountUserMapper;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public ApiResponseBody createOrder(WxPayOrderReqVO req, HttpServletRequest request) throws Exception {
        //生成商户订单号
        String tradeNo = getTradeNo();
        // 使用自动更新平台证书的RSA配置,配置微信支付的自动证书管理功能
        Config config =
                new RSAAutoCertificateConfig.Builder()
                        .merchantId(wxPayV3Bean.getMchId())
                        .privateKeyFromPath(wxPayV3Bean.getKeyPath())
                        .merchantSerialNumber(wxPayV3Bean.getMchSerialNo())
                        .apiV3Key(wxPayV3Bean.getApiKey())
                        .build();
        // 构建service,用于处理JSAPI支付相关的操作
        JsapiService service = new JsapiService.Builder().config(config).build();
        // 创建预支付订单的请求对象
        PrepayRequest prepayRequest = new PrepayRequest();
        Amount amount = new Amount();
        amount.setTotal(Integer.valueOf(req.getAmount().toString()));
        prepayRequest.setAmount(amount);
        prepayRequest.setAppid(wxPayV3Bean.getAppId());
        prepayRequest.setMchid(wxPayV3Bean.getMchId());
        prepayRequest.setNotifyUrl(wxPayV3Bean.getNotifyUrl());
        prepayRequest.setDescription(req.getDescription());
        prepayRequest.setOutTradeNo(tradeNo);
        prepayRequest.setAttach(req.getOrderType());
        //根据token拿到openid,指定该预支付订单的支付者身份
        String token = request.getHeader("token");
        Map<String, Object> openMap = wechatUserMapper.getOpenIdByToken(token);
        Payer payer = new Payer();
        payer.setOpenid(openMap.get("openId").toString());
        prepayRequest.setPayer(payer);

        // 调用下单方法,得到应答
        PrepayResponse response = service.prepay(prepayRequest);
        Map<String, Object> params = new HashMap<>();
        Long timeStamp = System.currentTimeMillis() / 1000;
        params.put("timeStamp", timeStamp);
        String substring = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
        params.put("nonceStr", substring);
        String signatureStr = Stream.of(wxPayV3Bean.getAppId(), String.valueOf(timeStamp), substring, "prepay_id=" + response.getPrepayId())
                .collect(Collectors.joining("\n", "", "\n"));
        String sign = WXPayUtil.getSign(signatureStr, wxPayV3Bean.getKeyPath());
        params.put("paySign", sign);
        params.put("package", "prepay_id=" + response.getPrepayId());
        params.put("tradeNo", tradeNo);
        log.info("-----------------------调用下单方法应答对象:" + params);

        //保存订单信息
        try {
            String accountUserId = accountUserMapper.getAccountUserId(req.getAccountNumber().toString());
            Map<String, Object> orderMap = new HashMap<>();
            orderMap.put("id", IdTool.getIdStr());
            orderMap.put("accountUserId", accountUserId);
            orderMap.put("appUserId", openMap.get("id"));
            orderMap.put("payOpenId", openMap.get("openId"));
            orderMap.put("orderType", req.getOrderType());
            orderMap.put("orderDate", DateUtil.getTime());
            orderMap.put("orderStatus", "0");
            orderMap.put("orderPrice", req.getAmount());
            orderMap.put("prepayId", response.getPrepayId());
            orderMap.put("tradeNo", tradeNo);
            log.info("-----------------------本次支付订单信息:" + orderMap);
            orderMapper.insertOrder(orderMap);
            return ApiResponseBody.defaultSuccess(params);
        } catch (Exception e) {
            throw new ApiException(e.toString());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ApiResponseBody  payNotify(HttpServletRequest request) throws Exception {
        //读取请求体的信息
        ServletInputStream inputStream = request.getInputStream();
        StringBuffer stringBuffer = new StringBuffer();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        String s;
        //读取回调请求体
        while ((s = bufferedReader.readLine()) != null) {
            stringBuffer.append(s);
        }
        String s1 = stringBuffer.toString();
        String timestamp = request.getHeader(WECHAT_PAY_TIMESTAMP);
        String nonce = request.getHeader(WECHAT_PAY_NONCE);
        String signType = request.getHeader("Wechatpay-Signature-Type");
        String serialNo = request.getHeader(WECHAT_PAY_SERIAL);
        String signature = request.getHeader(WECHAT_PAY_SIGNATURE);
        // 如果已经初始化了 RSAAutoCertificateConfig,可直接使用
        // 没有的话,则构造一个
        NotificationConfig config = new RSAAutoCertificateConfig.Builder()
                .merchantId(wxPayV3Bean.getMchId())
                .privateKeyFromPath(wxPayV3Bean.getKeyPath())
                .merchantSerialNumber(wxPayV3Bean.getMchSerialNo())
                .apiV3Key(wxPayV3Bean.getApiKey())
                .build();
        // 初始化 NotificationParser
        NotificationParser parser = new NotificationParser(config);
        RequestParam requestParam = new RequestParam.Builder()
                .serialNumber(serialNo)
                .nonce(nonce)
                .signature(signature)
                .timestamp(timestamp)
                // 若未设置signType,默认值为 WECHATPAY2-SHA256-RSA2048
                .signType(signType)
                .body(s1)
                .build();
        Transaction parse = parser.parse(requestParam, Transaction.class);
        log.info("-----------------------parse = " + parse);

        try {
            String status = orderMapper.queryPayOrderStatus(parse.getOutTradeNo().toString());
            if (status.equals("1")) {
                log.info("-----------------------该订单:" + parse.getOutTradeNo() + "已经进行回调,不可重复回调");
                return ApiResponseBody.defaultSuccess();
            }
            updateAccountDetail(parse);
        } catch (Exception e) {
            throw new ApiException(e.toString());
        }
        log.info("-----------------------回调完成-----------------------");
        return ApiResponseBody.defaultSuccess();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ApiResponseBody queryPayOrder(String tradeNo) {
         String status = orderMapper.queryPayOrderStatus(tradeNo);
         if (status.equals("1")) {
             return ApiResponseBody.defaultSuccess("SUCCESS");
         }
        // 使用自动更新平台证书的RSA配置,配置微信支付的自动证书管理功能
        Config config =
                new RSAAutoCertificateConfig.Builder()
                        .merchantId(wxPayV3Bean.getMchId())
                        .privateKeyFromPath(wxPayV3Bean.getKeyPath())
                        .merchantSerialNumber(wxPayV3Bean.getMchSerialNo())
                        .apiV3Key(wxPayV3Bean.getApiKey())
                        .build();
        // 构建service,用于处理JSAPI支付相关的操作
        JsapiService service = new JsapiService.Builder().config(config).build();

        //根据商户订单号查询支付订单
        QueryOrderByOutTradeNoRequest queryRequest = new QueryOrderByOutTradeNoRequest();
        queryRequest.setMchid(wxPayV3Bean.getMchId());
        queryRequest.setOutTradeNo(tradeNo);
        Transaction transaction = service.queryOrderByOutTradeNo(queryRequest);
        log.info("-----------------------支付状态:" + transaction.getTradeState());
        updateAccountDetail(transaction);
        return ApiResponseBody.defaultSuccess(transaction.getTradeState().toString());
    }

    /**
     * 时间+id为订单号
     * @param
     * @return
     */
    public String getTradeNo() {
        String idStr = IdTool.getIdStr();
        long timestamp = DateUtil.getDate();
//        //序列号是为了保证同一毫秒内生成的订单号的唯一性
//        AtomicInteger sequence = new AtomicInteger(0);
//        int nextSequence = sequence.getAndIncrement();
//
        try {
//            MessageDigest md = MessageDigest.getInstance("MD5");
//            byte[] messageDigest = md.digest(String.valueOf(nextSequence).getBytes());
//
//            BigInteger no = new BigInteger(1, messageDigest);
//            String encrypted = no.toString(10); // 将十六进制转为十进制表示的字符串
//
//            // 如果加密结果长度超过20位,则截取前20位
//            if (encrypted.length() > 20) {
//                encrypted = encrypted.substring(0, 20);
//            }
            String tradeNo = timestamp + idStr;
            return tradeNo;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public void updateAccountDetail(Transaction parse) {
        //更新订单状态
        Map<String, Object> map = new HashMap<>();
        map.put("tradeNo", parse.getOutTradeNo().toString());
        map.put("transactionId", parse.getTransactionId().toString());
        orderMapper.updateOrderStatus(map);
        //保存充值流水账单
        String outTradeNo = parse.getOutTradeNo();
        Map<String, Object> accountMap = accountUserMapper.getAccountUser(outTradeNo);
        Map<String, Object> billMap = new HashMap<>();
        billMap.put("id", IdTool.getIdStr());
        billMap.put("amount", parse.getAmount().getTotal());
        billMap.put("accountUserId", accountMap.get("accountUserId"));
        billMap.put("payOrderId", accountMap.get("payOrderId"));
        billMap.put("agoBalance", accountMap.get("balance"));
        billMap.put("createDate", DateUtil.getTime());
        billMap.put("laterBalance", BigDecimal.valueOf(parse.getAmount().getTotal()).add(BigDecimal.valueOf(Integer.valueOf(accountMap.get("balance").toString()))));
        billMap.put("billType", "pay");
        billMap.put("tradeType", "wechat-applet");
        log.info("-----------------------本次支付流水账单信息:" + billMap);
        orderMapper.insertBillLog(billMap);

        //更新户号的余额
        Map<String, Object> accountUserMap = new HashMap<>();
        accountUserMap.put("accountUserId", accountMap.get("accountUserId"));
        accountUserMap.put("balance", Long.valueOf(parse.getAmount().getTotal()) + Long.valueOf(accountMap.get("balance").toString()));
        accountUserMapper.updateBalance(accountUserMap);
    }
}

后面具体dao层xml里的sql就不具体写了。 

注意:这里有个特别大的一个坑,大家一定要注意。JSONObject千万要引fastjson2的包

微信支付 apiv3 java,微信小程序,微信,小程序,java,spring cloud,spring boot

至此完成!! 欢迎评论区讨论指出不足一起进步!!

做一个挑战风车的傻子,鲁莽也比怯懦更接近勇敢!文章来源地址https://www.toymoban.com/news/detail-768134.html

到了这里,关于【微信小程序】Java实现微信支付(小程序支付JSAPI-V3)java-sdk工具包的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 微信小程序JSAPI下单支付PHP前后端教程

    准备数据: 微信支付API证书,微信支付商户号,小程序appid,微信支付API证书序列号 获取微信支付api证书教程:如何下载微信支付证书(API证书)_荒~的博客-CSDN博客_微信支付证书 获取微信支付API证书序列号:点击管理证书即可看到   第一步:生成预支付交易单 参考文档:

    2024年02月11日
    浏览(56)
  • 微信小程序demo 调用支付jsapi缺少参数 total_fee,支付签名验证失败 究极解决方案

    其中package: ‘prepay_id=’ + data.prepayId,这一行代码,必须拼接‘prepay_id=’ 文档官方文档: https://pay.weixin.qq.com/docs/merchant/apis/jsapi-payment/jsapi-transfer-payment.html 注意看,后台生成sign时候,拼接的字符串中,也需要加上\\\"prepay_id=\\\" app支付和小程序支付的代码之前还有这种差别和坑…

    2024年02月04日
    浏览(52)
  • java对接微信支付:JSAPI支付(微信公众号支付)

    本文是【微信JSAPI支付】文章,主要讲解商户对接微信支付,简洁版测试 文章目录 一、JSAPI支付接入前准备 二、代码片段 1.引入Maven依赖 2.后端业务请求接口 3.前端调起支付请求方法 总结 1、JSAPI支付首先需要注册、认证一个公众号(大概300块一年) 微信公众号注册 2、申请成为

    2024年02月08日
    浏览(62)
  • Java实现微信小程序V3支付

    2024年02月12日
    浏览(61)
  • java对接微信支付:JSAPI支付成功之“微信回调”

    承接上一篇微信支付,现在简单说一下 微信支付回调 目录 一、支付回调 二、微信回调地址问题 1.本地/上线测试 2.控制器调用接口(代码) 总结 当用户支付成功之后,支付平台会向我们指定的服务器接口发送请求传递订单支付状态数据 如果你是再本地进行测试,那就需要使用

    2024年02月12日
    浏览(41)
  • 微信小程序基于java实现v2支付,提现,退款

    v2微信官方文档 封装支付请求实体 controller接口暴露层 payFoodOrder 支付接口实现类 获取请求ip wxform.setNotifyUrl(WechatUtil.getPayNotifyUrl() + WXPAY_NOTIFY_URL_FOOD_ORDER); 这个回调地址是你自己代码里面定义的回调接口,例如你定义的controller回调接口url是 feedback/wx/notifurl , 即是 wxform.setNoti

    2024年02月09日
    浏览(54)
  • Java实现微信小程序V3支付 (完整demo)

    2024年02月07日
    浏览(40)
  • 微信支付(JSAPI支付)/支付宝支付(手机网站支付)实现思路及实现方案-无源码

    停车系统 一个二维码同时支持微信及支付宝扫码付款,使用手机网站实现 临时车费用缴费二维码需要通知支持微信及支付宝。 用户缴纳的停车费直接到各个商户的账户上,不经过停车系统开发公司。 微信:使用微信服务商提供的服务 支付宝:使用支付服务商提供的服务 两

    2024年02月09日
    浏览(40)
  • 【微信支付】springboot-java接入微信支付-JSAPI支付/查单/退款/发送红包(二)---查单

    文章地址:https://blog.csdn.net/ssdadasd15623/article/details/134684556 查询订单分为微信订单号查询以及商户订单号查询,这里使用商户订单号,也就是自己的系统的订单号 https://pay.weixin.qq.com/docs/merchant/apis/jsapi-payment/query-by-out-trade-no.html 在请求接口时,注意⚠️:请求参数内的Authori

    2024年02月03日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包