微信native支付对接

简介

Native支付是指商户系统按微信支付协议生成支付二维码,用户再用微信“扫一扫”完成支付的模式。

应用场景

Native支付适用于PC网站、实体店单品或订单、媒体广告支付等场景,用户扫描商户展示在各种场景的二维码进行支付。聚体步骤如下:

1.商户根据微信支付的规则,为不同商品生成不同的二维码,展示在各种场景,用于用户扫描购买
2.用户使用微信“扫一扫”扫描二维码后,获取商品支付信息,引导用户完成支付。
3.用户确认支付,输入支付密码
4.支付完成后会提示用户支付成功,商户后台得到支付成功的通知,然后进行发货处理

接入前准备

直接跳转微信支付商户平台 https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_7_1.shtml

生成密钥文件:
在这里插入图片描述

配置文件:

wx:
  appId: appId
  keyPath: apiclient_key.pem
  certPath: apiclient_cert.pem
  certP12Path: 暂不用
  platformCertPath: platform_cert.pem
  mchId: mchId
  apiKey3: 暂不用
  apiKey: apiKey
  domain: https://hous.exchang.cn
  serialNo: 序列号

pom文件:

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

代码:

配置类:

//获取yml中微信配置
@Data
@ConfigurationProperties(prefix = "wx")
public class WechatProperties {

    private String appId;

    private String keyPath;

    private String certPath;

    private String platformCertPath;

    private String mchId;

    private String apiKey;

    private String domain;

    private String serialNo;
}
//配置类
@Configuration
@EnableConfigurationProperties(WechatProperties.class)
public class WechatNativeConfig {


    private final WechatProperties wechatProperties;

    public WechatNativeConfig(WechatProperties wechatProperties) {
        this.wechatProperties = wechatProperties;
    }

    @Bean
    public RSAConfig rsaConfig() throws IOException {
        ClassPathResource keyResource = new ClassPathResource(wechatProperties.getKeyPath());
        String apiClientKey = keyResource.getFile().getPath();
        ClassPathResource certResource = new ClassPathResource(wechatProperties.getPlatformCertPath());
        String platformCertResourceKey = certResource.getFile().getPath();
        /*String apiClientKey = wechatProperties.getKeyPath();
//        ClassPathResource certResource = new ClassPathResource(wechatProperties.getPlatformCertPath());
        String platformCertResourceKey = wechatProperties.getPlatformCertPath();*/
        return new RSAConfig.Builder()
                .merchantId(wechatProperties.getMchId())
                .privateKeyFromPath(apiClientKey)
                .merchantSerialNumber(wechatProperties.getSerialNo())
                //平台证书
                .wechatPayCertificatesFromPath(platformCertResourceKey)
                .build();
    }

    @Bean
    public NativePayService nativePayService() throws IOException {
        return new NativePayService.Builder()
                .config(this.rsaConfig())
                .build();
    }

    @Bean
    public NotificationParser notificationParser() throws IOException{
        ClassPathResource certResource = new ClassPathResource(wechatProperties.getPlatformCertPath());
        String platformCertResourceKey = certResource.getFile().getPath();
        //String platformCertResourceKey = wechatProperties.getPlatformCertPath();
        NotificationConfig config = new RSANotificationConfig.Builder()
                .apiV3Key(wechatProperties.getApiKey())
                .certificatesFromPath(platformCertResourceKey)
                .build();
        // 初始化 NotificationParser
        return new NotificationParser(config);

    }

    @Bean
    public RefundService refundService() throws IOException {
        return new RefundService.Builder().config(this.rsaConfig()).build();
    }

}

创建支付单:

	@Slf4j
@Service
@EnableConfigurationProperties(WechatProperties.class)
public class WechatNativePayServiceImpl implements BasePayService {

    @Resource
    private WechatProperties wechatProperties;

    @Resource
    private NativePayService nativePayService;

    @Resource
    private RedisCache redisCache;

    @Value("${spring.application.name:scm-ofc-system}")
    private String serviceName;


    @Override
    public AjaxResult<?> payOrder(List<OrderSub> orderSubs) {
    	//总金额,单位分
        Integer payTotal = total.multiply(BigDecimal.valueOf(100)).intValue();
        PrepayRequest prepayRequest = new PrepayRequest();
        prepayRequest.setAppid(wechatProperties.getAppId());
        prepayRequest.setMchid(wechatProperties.getMchId());
        prepayRequest.setDescription("供应链下单");
        //订单编号
        prepayRequest.setOutTradeNo(orderSub.getOrderMsn());
        prepayRequest.setNotifyUrl(wechatProperties.getDomain() + "/" + serviceName + "/callback/wechat/pay");
        prepayRequest.setAttach(BusType.CREATE_ORDER.getDesc());
        Amount amount = new Amount();
        amount.setTotal(payTotal);
        prepayRequest.setAmount(amount);
        log.info("创建预支付单入参:【{}】", JSONObject.toJSONString(prepayRequest));
        try {
            PrepayResponse prepay = nativePayService.prepay(prepayRequest);
            log.info("创建预支付单并生成二维码成功,出参:【{}】", JSONObject.toJSONString(prepay));
            Map<String, String> hashMap = new HashMap<>();
            hashMap.put("orderMsn", orderSub.getOrderMsn());
            hashMap.put("codeUrl", prepay.getCodeUrl());
            //二维码暂存redis
            String value = JSONObject.toJSONString(hashMap);
            redisCache.setCacheObject(RedisKeyConstant.ORDER_PAY_URL_CODE + orderSub.getOrderMsn(), value, 15, TimeUnit.MINUTES);
            return AjaxResult.ok(hashMap);
        } catch (HttpException e) { // 发送HTTP请求失败
            log.error("发送HTTP请求失败:{}", e.getHttpRequest());
            return AjaxResult.fail("发送HTTP请求失败");
        } catch (ServiceException e) { // 服务返回状态小于200或大于等于300,例如500
            log.error("微信支付返回状态异常,{}", e.getResponseBody());
            return AjaxResult.fail("微信支付返回状态异常");
        } catch (MalformedMessageException e) { // 服务返回成功,返回体类型不合法,或者解析返回体失败
            log.error("返回类型不合法:{}", e.getMessage());
            return AjaxResult.fail("返回类型不合法");
        }
    }

    @Override
    public void closeOrder(String outTradeNo) {
        CloseOrderRequest closeOrderRequest = new CloseOrderRequest();
        closeOrderRequest.setMchid(wechatProperties.getMchId());
        closeOrderRequest.setOutTradeNo(outTradeNo);
        nativePayService.closeOrder(closeOrderRequest);
        log.info("订单关闭成功,入参:【{}】", JSONObject.toJSONString(closeOrderRequest));
    }

    @Override
    public Optional<Transaction> getOrderInfo(String outTradeNo) {
        QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
        request.setOutTradeNo(outTradeNo);
        request.setMchid(wechatProperties.getMchId());
        Transaction transaction = nativePayService.queryOrderByOutTradeNo(request);
        if (Objects.isNull(transaction)) {
            return Optional.empty();
        }
        return Optional.of(transaction);
    }

}

创建退款单:

@Slf4j
@Service
@EnableConfigurationProperties(WechatProperties.class)
public class WechatNativeRefundServiceImpl implements BaseRefundService {

    @Resource
    private RefundService refundService;

    @Resource
    private WechatProperties wechatProperties;

    @Resource
    private BaseOrderFeignService baseOrderFeignService;

    @Resource
    private RefundOrderFeignService refundOrderFeignService;

    @Value("${spring.application.name:scm-ofc-system}")
    private String serviceName;

    @Override
    public AjaxResult<?> refundOrder(AsRefundApply asRefundApply) {
        try {
            BigDecimal refundAmount = asRefundApply.getRefundAmount();
            String orderSsn = asRefundApply.getOrderSsn();
            Optional<OrderSub> data = baseOrderFeignService.getOrderSubDetail(orderSsn).getData();
            if (!data.isPresent()) {
                return AjaxResult.fail(500, "当前单不存在");
            }
            //退款金额
            long refundTotal = refundAmount.multiply(BigDecimal.valueOf(100)).longValue();
            OrderSub orderSub = data.get();
            //原支付金额
            String orderMsn = orderSub.getOrderMsn();
            List<OrderSub> orderSubList = baseOrderFeignService.selectOrderSubs(orderMsn).getData();
            BigDecimal officialReceipts = orderSubList.stream().map(OrderSub::getOfficialReceipts).reduce(BigDecimal.ZERO, BigDecimal::add);
            long payTotal = officialReceipts.multiply(BigDecimal.valueOf(100)).longValue();
            //创建微信退款单
            CreateRequest createRequest = new CreateRequest();
            AmountReq amountReq = new AmountReq();
            amountReq.setCurrency("CNY");
            amountReq.setTotal(payTotal);
            amountReq.setRefund(refundTotal);
            createRequest.setOutTradeNo(orderSub.getOrderMsn());
            createRequest.setAmount(amountReq);
            createRequest.setOutRefundNo(asRefundApply.getAfterSaleSn());
            createRequest.setNotifyUrl(wechatProperties.getDomain() + "/" + serviceName +  "/callback/wechat/refund");
            log.info("退款单入参:{}", JSONObject.toJSONString(createRequest));
            Refund refund = refundService.create(createRequest);
            log.info("创建退款单成功:{}", JSONObject.toJSONString(refund));
            if (Objects.isNull(refund)) {
                log.error("退款异常,参数:{}", JSONObject.toJSONString(createRequest));
                return AjaxResult.fail(500, "退款异常,请求微信返回值为null:参数" + JSONObject.toJSONString(createRequest));
            }
            if (Objects.equals(refund.getStatus(), Status.SUCCESS)) {
                return AjaxResult.ok(refund);
            }
        } catch (Exception e) {
            log.error("退款异常:{}", e.getMessage());
            return AjaxResult.fail(500, "退款异常,请求微信报错" + e.getMessage());
        }
        return AjaxResult.ok();
    }

    @Override
    public AjaxResult<?> refundAllOrder(List<OrderSub> orderSubList) {
        try {
            OrderSub orderSub = orderSubList.get(NumberUtils.INTEGER_ZERO);
            BigDecimal officialReceipts = orderSubList.stream().map(OrderSub::getOfficialReceipts).reduce(BigDecimal.ZERO, BigDecimal::add);
            long payTotal = officialReceipts.multiply(BigDecimal.valueOf(100)).longValue();
            //创建微信退款单
            CreateRequest createRequest = new CreateRequest();
            AmountReq amountReq = new AmountReq();
            amountReq.setCurrency("CNY");
            amountReq.setTotal(payTotal);
            amountReq.setRefund(payTotal);
            createRequest.setOutTradeNo(orderSub.getOrderMsn());
            createRequest.setAmount(amountReq);
            createRequest.setOutRefundNo("ALL" + orderSub.getOrderMsn());
            createRequest.setNotifyUrl(wechatProperties.getDomain() + "/callback/wechat/refund");
            Refund refund = refundService.create(createRequest);
            log.info("创建退款单成功:{}", JSONObject.toJSONString(refund));
            if (Objects.isNull(refund)) {
                log.error("退款异常,参数:{}", JSONObject.toJSONString(createRequest));
                return AjaxResult.fail(500, "退款异常,请求微信返回值为null:参数" + JSONObject.toJSONString(createRequest));
            }
            if (Objects.equals(refund.getStatus(), Status.SUCCESS)) {
                return AjaxResult.ok(refund);
            }
        } catch (Exception e) {
            log.error("退款异常:{}", e.getMessage());
            return AjaxResult.fail(500, "退款异常,请求微信报错" + e.getMessage());
        }
        return AjaxResult.ok();
    }

    /**
     * 查询微信退款单详情
     *
     * @param afterSaleSn
     */
    @Override
    public Refund getRefundOrderDetail(String afterSaleSn) {
        QueryByOutRefundNoRequest request = new QueryByOutRefundNoRequest();
        request.setOutRefundNo(afterSaleSn);
        return refundService.queryByOutRefundNo(request);
    }

    @Override
    public AjaxResult<?> returnPayOrder(String orderSn) {
        Optional<OrderSub> optional = baseOrderFeignService.getOrderSubDetail(orderSn).getData();
        if (!optional.isPresent()) {
            throw new ServiceException("订单信息不存在");
        }
        OrderSub orderSub = optional.get();
        String orderSsn = orderSub.getOrderSsn();
        AjaxResult<AsRefundApply> ajaxResult = refundOrderFeignService.createReturnOrder(orderSsn);
        if (ajaxResult.isSuccess()) {
            AsRefundApply asRefundApply = ajaxResult.getData();
            AjaxResult<?> result = this.refundOrder(asRefundApply);
            if (result.isError()) {
                //refundOrderFeignService.deleteReturnOrder();
                throw new ServiceException(result.getMsg());
            }
        }
        return ajaxResult;
    }
}

支付与退款回调

public interface CallbackService {

    /**
     * 微信支付回调
     * @param request
     * @param response
     * @return
     */
    void  wechatPayCallback(HttpServletRequest request, HttpServletResponse response);


    /**
     * 微信退款回调
     * @param request
     * @param response
     */
    void wechatRefundCallback(HttpServletRequest request, HttpServletResponse response);
}

//实现类
@Slf4j
@Service
public class CallbackServiceImpl implements CallbackService {

    @Resource
    private NotificationParser notificationParser;

    @Override
    public void wechatPayCallback(HttpServletRequest request, HttpServletResponse response) {
        try {
            log.info("=========================微信native支付回调通知============================");
            Transaction transaction = this.verifyAndDecrypt(request, Transaction.class);
            log.info("验证签名成功:{}", JSONObject.toJSONString(transaction));
            Transaction.TradeStateEnum tradeState = transaction.getTradeState();
            if (!Objects.equals(tradeState, Transaction.TradeStateEnum.SUCCESS)) {
                return;
            }
            //支付业务
        } catch (Exception e) {
            log.error("支付回调异常:{}", e.getMessage());
        }
    }

    @Override
    public void wechatRefundCallback(HttpServletRequest request, HttpServletResponse response) {
        log.info("=========================微信native退款回调通知============================");
        RefundNotification refundNotification = this.verifyAndDecrypt(request, RefundNotification.class);
        Status refundStatus = refundNotification.getRefundStatus();
        if (!Objects.equals(refundStatus, Status.SUCCESS)) {
            return;
        }
    }
    /**
     * 验证并解密报文
     *
     * @param request
     */
    private <T> T verifyAndDecrypt(HttpServletRequest request, Class<T> clazz) {
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        String nonce = request.getHeader("Wechatpay-Nonce");
        String serialNo = request.getHeader("Wechatpay-Serial");
        String signature = request.getHeader("Wechatpay-Signature");
        String signType = request.getHeader("Wechatpay-Signature-Type");
        String body = this.getBody(request);
        log.info("\n请求头信息:\n" +
                        "Wechatpay-Timestamp:{},\n" +
                        "Wechatpay-Nonce:{},\n" +
                        "Wechatpay-Serial:{},\n" +
                        "Wechatpay-Signature:{},\n" +
                        "Wechatpay-Signature-Type:{},\n" +
                        "body: {}",
                timestamp, nonce, serialNo, signature, signType, body);
        RequestParam requestParam = new RequestParam.Builder()
                .serialNumber(serialNo)
                .nonce(nonce)
                .signature(signature)
                .timestamp(timestamp)
                .signType(signType)
                .body(body)
                .build();
        return notificationParser.parse(requestParam, clazz);
    }

    /**
     * 获取请求体内容
     *
     * @param request
     * @return
     */
    private String getBody(HttpServletRequest request) {
        BufferedReader reader;
        String body = "";
        try {
            reader = request.getReader();
            String line;
            StringBuilder inputString = new StringBuilder();
            while ((line = reader.readLine()) != null) {
                inputString.append(line);
            }
            body = inputString.toString();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return body;
    }
 }

以上就是微信native支付的对接,仅供参考

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值