Java实现微信支付

微信支付

小黄最在工作中对接需要对接微信支付,在此记录一下微信支付开发的相关流程,望能帮助到各位

前期准备

由于小黄是小程序端需要对接微信支付,需要注册小程序等,小黄会一一列举出来

小程序注册

所需文件
  • 没有注册过微信公众平台、微信开放平台的邮箱
  • 营业执照
  • 对公账户信息(需要对公账户打款来校验)
  • 纳税识别号
  • 300大洋
注册

注册网址:https://mp.weixin.qq.com/cgi-bin/wx

按要求填写所需文件信息即可,由于是企业注册的小程序,需要对公账户汇款,只需要几毛钱并且这个钱会退回来的

填写小程序信息

注册完成后,开始填写小程序信息,必须填写小程序信息,才能获得appid,也就是小程序id,开发中会用到

开通微信认证

这里需要支付300元的费用

微信支付注册

目前只支持企业注册微信支付

所需文件
  • 营业执照照片
  • 法人身份证照片
  • 所属行业的特殊资质
  • 超级管理员证明(需要公章)
  • 结算账户(对公账户)
注册

注册网址:https://pay.weixin.qq.com/index.php/apply/applyment_home/guide_normal

同样也是按要求填写信息即可,也是需要对公账户汇款

申请api证书

在开发中需要读取到api证书来解析你的api是否正确,所以这一步是必不可少的,另外推荐设置APIv3密钥,功能比v2版本多

image-20230914164243283

绑定微信小程序

在产品中心里,可以绑定微信小程序

image-20231005110030294

开发微信支付

微信支付有很多支付方式,具体可以参考产品文档,因为业务需求,小黄使用的是JSAPI支付方式,接下来就重点介绍一下JSAPI支付方式。

接入前准备

需要设置支付授权目录,这里小黄设置的就是公司测试域名

image-20231005110519599

引入maven

<dependency>
    <groupId>com.github.wechatpay-apiv3</groupId>
    <artifactId>wechatpay-java</artifactId>
    <version>0.2.11</version>
</dependency>

支付

刚开始对接微信支付,咱们肯定先对接支付,看一下JSAPI支付的时序图,其实需要Java后端做的就只有2-6,15-21

示意图

微信创建订单

参考文档

在创建订单之前的业务处理,小黄就不贴出来了,每个开发的业务都不一样,主要是JSAPI下单给微信接受他的返回值,也就是时序图中的2-6步骤

创建配置类

商户证书序列号,可以通过执行openssl x509 -noout -serial -in apiclient_cert.pem指令解析出来,cmd到证书所在位置

image-20231005112010001

@Configuration
@Data
@RefreshScope
public class WechatConfig {
    @Value("${wechat.mchId}")
    private String mchId; //微信支付id

    @Value("${wechat.filePath}")
    private String filePath; //微信支付证书所存放的位置

    @Value("${wechat.merchantSerialNumber}")
    private String merchantSerialNumber; //商户证书序列号

    @Value("${wechat.apiV3Key}")
    private String apiV3Key; //apiv3密钥

    @Value("${wechat.appId}")
    private String appId; //绑定的小程序id

    @Value("${wechat.notify-url}")
    private String notifyUrl; //支付回调地址(目前用不到)

    @Value("${wechat.refund-notify-url}")
    private String refundNotifyUrl; //退款回调地址(目前用不到)

    @Bean
    public RSAAutoCertificateConfig rsaAutoCertificateConfig(){
        return new RSAAutoCertificateConfig.Builder()
                .merchantId(mchId)
                .merchantSerialNumber(merchantSerialNumber)
                .apiV3Key(apiV3Key)
                .privateKeyFromPath(filePath)
                .build();
    }
}

通过api调用微信支付

之前小黄看到好多文档都是通过http请求来调用的,加了依赖后,相当于微信封装了一层,使用起来更方便了,只需要向PrepayRequest中塞数据即可

这里有两个注意点:

  • 金额的单位是分
  • 必须要填异步回调地址(回调地址的作用是当用户支付后,会访问这个地址访问支付信息,该地址必须是https,测试的时候建议使用花生壳工具走内网穿透)
public PrepayWithRequestPaymentResponse prepay(JsPrepayDto jsPrepayDto, MemberUserEntity memberUser) {
    //下单并生成调起支付的参数
    JsapiServiceExtension service = new JsapiServiceExtension.Builder().config(config).build();

    PrepayRequest request = new PrepayRequest();
    request.setAppid(wechatConfig.getAppId());
    request.setMchid(wechatConfig.getMchId());
    request.setOutTradeNo(jsPrepayDto.getTradeNo()); //传入自己平台的订单id
    request.setDescription(jsPrepayDto.getDescription());
    request.setNotifyUrl(wechatConfig.getNotifyUrl());
    Amount amount = new Amount();
    //todo:测试只支付一分
    amount.setTotal(1);
    request.setAmount(amount);
    Payer payer = new Payer();
    payer.setOpenid(memberUser.getWxOpenId());
    request.setPayer(payer);
    PrepayWithRequestPaymentResponse response = service.prepayWithRequestPayment(request);

    return response;
}
支付通知

参考文档

创建订单完成后,微信会主动调用设置的回调地址,将加密的数据返回给我们

注意

对后台通知交互时,如果微信收到应答不是成功或超时,微信认为通知失败,微信会通过一定的策略定期重新发起通知,尽可能提高通知的成功率,但微信不保证通知最终能成功

  • 同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。 推荐的做法是,当商户系统收到通知进行处理时,先检查对应业务数据的状态,并判断该通知是否已经处理。如果未处理,则再进行处理;如果已处理,则直接返回结果成功。在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱。
  • 如果在所有通知频率后没有收到微信侧回调。商户应调用查询订单接口确认订单状态。

特别提醒: 商户系统对于开启结果通知的内容一定要做签名验证,并校验通知的信息是否与商户侧的信息一致,防止数据泄露导致出现“假通知”,造成资金损失。

解析请求

private String getRequestBody(HttpServletRequest request) {
    StringBuffer sb = new StringBuffer();
    try (ServletInputStream inputStream = request.getInputStream();
         BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
    ) {
        String line;

        while ((line = reader.readLine()) != null) {
            sb.append(line);
        }
    } catch (IOException e) {
        log.error("读取数据流异常:{}", e);
    }
    return sb.toString();
}

解密、处理业务

Transaction类有很多,注意是com.wechat.pay.java.service.payments.model包下的

@Transactional(rollbackFor = Exception.class)
public Map wechatNotify(HttpServletRequest request, HttpServletResponse response) {
    HashMap<String, String> map = new HashMap<>();
    //获取报文
    String body = getRequestBody(request);
    //随机串
    String nonceStr = request.getHeader("Wechatpay-Nonce");

    //微信传递过来的签名
    String signature = request.getHeader("Wechatpay-Signature");

    //证书序列号(微信平台)
    String serialNo = request.getHeader("Wechatpay-Serial");

    //时间戳
    String timestamp = request.getHeader("Wechatpay-Timestamp");
    // 构造 RequestParam
    RequestParam requestParam = new RequestParam.Builder()
            .serialNumber(serialNo)
            .nonce(nonceStr)
            .signature(signature)
            .timestamp(timestamp)
            .body(body)
            .build();
    // 初始化 NotificationParser
    NotificationParser parser = new NotificationParser(config);
    Transaction transaction = null;
    try {
        // 验签、解密并转换成 Transaction
        transaction = parser.parse(requestParam, Transaction.class);
        log.info("=============解密:Transaction ============= {}", transaction);
    } catch (Exception e) {
        log.error("签名验证失败");
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        map.put("code", "FAIL");
        map.put("message", "失败");
        return map;
    }

    String lockName = "wechat_notice_" + transaction.getOutTradeNo();
    RLock lock = redissonClient.getLock(lockName);
    try {
        //微信可能会重复发送数据,如果已经处理过,应该直接返回成功
        //加锁,避免并发重复添加
        lock.lock(5,TimeUnit.SECONDS);
        log.info("=============接收到微信支付回调通知=============");
        
        //-----处理业务逻辑-----
       
        //通知微信回调成功
        map.put("code", "SUCCESS");
        return map;
    } catch (Exception e) {
        log.error("业务处理失败");
        response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        map.put("code", "FAIL");
        map.put("message", "失败");
        return map;
    } finally {
        lock.unlock();
    }
}
主动查询订单

参考文档

这个请求可以查询订单的支付状态

@Transactional(rollbackFor = Exception.class)
public void findPaymentOrder(String payOrderId) {
    JsapiServiceExtension service = new JsapiServiceExtension.Builder().config(config).build();
    QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
    request.setMchid(wechatConfig.getMchId());
    request.setOutTradeNo(payOrderId);
    Transaction transaction = service.queryOrderByOutTradeNo(request);
    log.info("============= Transaction ============= {}", transaction);
    String lockName = "wechat_notice_" + transaction.getOutTradeNo();
    RLock lock = redissonClient.getLock(lockName);
    try {
        //加锁,避免并发重复添加
        lock.lock(5,TimeUnit.SECONDS);
       	//处理业务逻辑
    } finally {
        lock.unlock();
    }
}

退款

支付功能完成后,咱们开始着手做退款功能,其实跟支付差不多,先是去创建退款订单,再来接受微信发起的退款回调

创建退款订单

参考文档

这里需要注意的是,创建退款订单会有一个返回对象,里面包含着请求信息,但是微信退款是有延迟的,测试的时候到账时间在1-10秒内,如果要在到账后在做操作,建议通过回调来实现

public Boolean refundOrder(WechatRefundReq data) {
    //查询支付订单信息
    PayOrderDetailsEntity order = payOrderDetailsDao.selectOne(new LambdaQueryWrapper<PayOrderDetailsEntity>().eq(PayOrderDetailsEntity::getPayOrderId, data.getPayOrderId()));
    if (data.getRefundAmount().compareTo(order.getPayAmount()) > 0) {
        log.error("退款金额超出订单金额:data : {} , order : {}", data, order);
        return false;
    }
    //退款接口
    RefundService service = new RefundService.Builder().config(config).build();
    CreateRequest request = new CreateRequest();
    request.setTransactionId(order.getTradeNo());
    request.setOutRefundNo(data.getRefundOrderId());
    //用户退款原因,会在用户端微信提示
//        request.setReason(data.getReason());
    request.setNotifyUrl(wechatConfig.getRefundNotifyUrl());
    AmountReq amount = new AmountReq();
    amount.setRefund(data.getRefundAmount().multiply(BigDecimal.valueOf(100)).longValue());
    amount.setTotal(order.getPayAmount().multiply(BigDecimal.valueOf(100)).longValue());
    amount.setCurrency("CNY");
    request.setAmount(amount);
    log.info("======开始处理微信退款======");
    Refund refund = service.create(request);
    log.info("refund : {}" ,refund);

    return true;
}
退款通知

参考文档

注意

对后台通知交互时,如果微信收到应答不是成功或超时,微信认为通知失败,微信会通过一定的策略定期重新发起通知,尽可能提高通知的成功率,但微信不保证通知最终能成功

  • 同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。 推荐的做法是,当商户系统收到通知进行处理时,先检查对应业务数据的状态,并判断该通知是否已经处理。如果未处理,则再进行处理;如果已处理,则直接返回结果成功。在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱。
  • 如果在所有通知频率后没有收到微信侧回调。商户应调用查询订单接口确认订单状态。

特别提醒: 商户系统对于开启结果通知的内容一定要做签名验证,并校验通知的信息是否与商户侧的信息一致,防止数据泄露导致出现“假通知”,造成资金损失。

@Transactional(rollbackFor = Exception.class)
public Map wechatNotify(HttpServletRequest request, HttpServletResponse response) {
    HashMap<String, String> map = new HashMap<>();
    //获取报文
    String body = getRequestBody(request);
    //随机串
    String nonceStr = request.getHeader("Wechatpay-Nonce");

    //微信传递过来的签名
    String signature = request.getHeader("Wechatpay-Signature");

    //证书序列号(微信平台)
    String serialNo = request.getHeader("Wechatpay-Serial");

    //时间戳
    String timestamp = request.getHeader("Wechatpay-Timestamp");
    // 构造 RequestParam
    RequestParam requestParam = new RequestParam.Builder()
            .serialNumber(serialNo)
            .nonce(nonceStr)
            .signature(signature)
            .timestamp(timestamp)
            .body(body)
            .build();
    // 初始化 NotificationParser
    NotificationParser parser = new NotificationParser(config);
    Transaction transaction = null;
    try {
        // 验签、解密并转换成 Transaction
        transaction = parser.parse(requestParam, Transaction.class);
        log.info("=============解密:Transaction ============= {}", transaction);
    } catch (Exception e) {
        log.error("签名验证失败");
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        map.put("code", "FAIL");
        map.put("message", "失败");
        return map;
    }

    String lockName = "wechat_notice_" + transaction.getOutTradeNo();
    RLock lock = redissonClient.getLock(lockName);
    try {
        //微信可能会重复发送数据,如果已经处理过,应该直接返回成功
        //加锁,避免并发重复添加
        lock.lock(5,TimeUnit.SECONDS);
        log.info("=============接收到微信支付回调通知=============");
        
        //-----处理业务逻辑-----
        
        //通知微信回调成功
        map.put("code", "SUCCESS");
        return map;
    } catch (Exception e) {
        log.error("业务处理失败");
        response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        map.put("code", "FAIL");
        map.put("message", "失败");
        return map;
    } finally {
        lock.unlock();
    }
}

总结

至此,微信支付的主要流程就已经走完了,其他的api可以参考官方文档,对照着SDK来做,应该是轻而易举的

  • 6
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
### 回答1: 在 Java实现微信支付需要使用微信支付 SDK。您可以在微信支付官方网站上下载最新版本的 SDK,并在您的 Java 项目中使用它。 使用微信支付 SDK 的具体步骤如下: 1. 注册微信支付商户并获取商户 ID 和密钥。 2. 下载并导入微信支付 SDK。 3. 创建支付订单并获取支付二维码。 4. 使用扫码支付或者 JSAPI 支付接口调用微信支付。 5. 接收微信支付的异步通知并进行订单状态的更新。 有关微信支付的更多信息,您可以参考微信支付官方文档:https://pay.weixin.qq.com/wiki/doc/api/index.html 希望这些信息对您有帮助! ### 回答2: 要实现微信支付功能,可以使用Java编程语言结合微信支付开放平台提供的API来实现。 首先,需要在微信支付开放平台注册并创建一个应用,获取到应用的AppID、商户号、AppSecret等信息。 接下来,在Java项目中引入微信支付SDK,可以使用第三方库,如"wechatpay-api"来简化开发流程。然后,通过使用SDK提供的相关方法,可以实现以下功能: 1. 获取access_token:通过获取 access_token 接口,获取微信支付接口调用凭证,用于后续接口调用的身份验证。 2. 统一下单:使用统一下单接口,将用户提交的支付信息传给微信支付平台,生成预支付订单,并返回预付款二维码链接或者支付ID。 3. 生成支付链接或二维码:将预付款二维码链接或支付ID返回给前端,通过生成支付链接或者二维码的方式,提供给用户进行支付。 4. 微信支付回调:在用户支付成功后,微信支付平台会异步调用开发者设置的支付结果通知地址,向该地址发送支付结果信息。开发者需要在接收到回调时,验证回调的合法性,并及时处理支付结果。 5. 查询订单状态:通过订单查询接口,可以查询用户支付订单的当前状态,如支付成功、支付失败等。 6. 退款:使用退款接口,可以实现订单退款操作,退还用户支付的金额。 以上是使用Java实现微信支付的基本步骤。在具体实现过程中,还需注意接口调用的参数传递、异常处理、数据加密等问题。为确保支付安全,建议使用HTTPS协议进行数据传输,并加强对接口的签名验证。 ### 回答3: Java 实现微信支付可以通过微信支付开放平台提供的开发工具包来完成。首先,需要在微信支付开放平台注册一个开发者账号,并创建一个应用获取对应的应用ID和应用密钥。 接下来,可以使用Java语言进行开发。首先,需要引入微信支付Java SDK,例如官方提供的weixin-java-pay SDK。通过在项目的pom.xml文件中添加相关依赖,即可将SDK集成到项目中。 然后,通过在代码中进行配置,将应用ID和应用密钥等信息设置到SDK中。可以使用SDK提供的配置类进行设置,例如WxPayConfig。 接下来,可以使用SDK提供的API进行微信支付的相关操作。例如,使用SDK提供的统一下单API可以生成一个支付链接,通过该链接用户可以进行支付操作。可以通过创建对应的请求对象,并调用SDK提供的发送请求的方法来实现。 支付结果通知可以使用SDK提供的回调接口来处理,当支付完成后,微信支付平台会向预先设置的回调URL发送通知。可以在代码中编写对应的处理逻辑,接收并解析微信支付平台发送的通知,校验数据的正确性,并进行相应的业务处理。 最后,可以使用SDK提供的查询订单API来查询订单的支付结果。可以根据订单号或其他查询条件,调用SDK提供的查询订单接口来获取订单的最新支付状态。 通过以上步骤,就可以使用Java实现微信支付功能。需要注意的是,开发过程中需要仔细阅读微信支付开放平台提供的文档和SDK的使用指南,并按照实际需求进行相关配置和操作。同时,为了确保支付过程的安全性,建议使用SSL证书进行加密传输。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值