WxJava desarrolla el inicio de sesión y el pago de WeChat


Prefacio

WxJava es un kit de herramientas de desarrollo de WeChat Java que admite el desarrollo back-end de módulos funcionales de WeChat, incluidos pagos de WeChat, plataforma abierta, cuenta pública, WeChat empresarial/cuenta empresarial, miniprogramas y otros módulos de funciones de WeChat.

nombre sitio web
Sitio web oficial de la casa rural https://gitee.com/binary/weixin-java-tools
Documentación en línea de WxJava http://wxjava.fly2you.cn/zh-CN/start/pay/explain.html
Wiki de documentación de desarrollo https://github.com/wechat-group/WxJava/wiki
javadoc weixin-java-miniapp , weixin-java-pay , weixin-java-mp, weixin-java-common , weixin-java-cp , weixin-java-open
Documento de pago de WeChat https://pay.weixin.qq.com/docs/merchant/apis/mini-program-paid/mini-prepay.html
Documento de inicio de sesión de WeChat https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/user-login/code2Session.html

Trabajo de preparación
El pago de WeChat debe abrir los comerciantes de pagos de WeChat. Consulte el sitio web oficial: https://pay.weixin.qq.com/docs/merchant/products/jsapi-paid/preparation.html

1. Introducir dependencias

La versión que estoy usando aquí es 4.5.0

<dependency>
    <groupId>com.github.binarywang</groupId>
    <artifactId>weixin-java-pay</artifactId>
    <version>${weixin-java.version}</version>
</dependency>
<dependency>
    <groupId>com.github.binarywang</groupId>
    <artifactId>weixin-java-miniapp</artifactId>
    <version>${weixin-java.version}</version>
</dependency>

2. Modificar el archivo de configuración.

La información de configuración aquí ha sido modificada y no se puede usar directamente.
El pago de WeChat usa la versión apiV3.
La ruta del certificado puede ser una ruta absoluta o una ruta de clase. Lo puse en /resource/cert del proyecto.

wx:
  miniapp:
    configs:
      - appid: wx2xxxxxxxx
        secret: f7bd3ed88cxxxxxxx222e1a7e4f722ad9
        msgDataFormat: JSON
  pay:
    appId: wx2d0f68xxx7f #微信公众号或者小程序等的appid
    mchId: 1650000080 #微信支付商户号
    apiV3Key: TWBQNkNwwjxxxxx2hN5oQ
    certSerialNo: 2078791B21788DC90E44xxxxxx7291FFD
    privateKeyPath: classpath:cert/apiclient_key.pem #apiclient_key.pem证书文件的绝对路径或者以classpath:开头的类路径
    privateCertPath: classpath:cert/apiclient_cert.pem #apiclient_cert.pem证书文件的绝对路径或者以classpath:开头的类路径
    notifyUrl: https://35fxxxxxpbf.guyubao.com/anonymous/wx/notify/order

3. Inicio de sesión en el miniprograma WeChat

1.Tiempo del proceso de inicio de sesión

Insertar descripción de la imagen aquí

2. Comprenda openid, unionid y código

1.openid

openid es una cadena utilizada para identificar de forma única a un usuario. En el subprograma WeChat, el openid de cada usuario es único. A través de openid, el mini programa puede obtener la información básica del usuario, como avatar, apodo, etc.

Nota: El mismo usuario tiene diferentes openids en diferentes miniprogramas. Por lo tanto, al desarrollar programas pequeños, openid no se puede utilizar para determinar la singularidad de los usuarios.

2. sindicatos

Unionid es una cadena que se utiliza para identificar de forma única al usuario cuando éste vincula varias aplicaciones bajo la misma cuenta de plataforma abierta WeChat. Si un usuario usa el mismo ID de WeChat para la autorización de inicio de sesión en múltiples miniprogramas, el unionid en estos miniprogramas será el mismo.

Nota: El unionid del usuario solo se generará cuando el usuario vincule varias aplicaciones a la misma cuenta de plataforma abierta WeChat. Por lo tanto, si el usuario no está vinculado a varias aplicaciones, el subprograma no podrá obtener el ID de unión del usuario.

3. código

El código es la credencial de inicio de sesión del usuario, que el servidor WeChat emite al miniprograma. Una vez que el usuario autoriza el inicio de sesión, el subprograma puede obtener el código del usuario llamando a la interfaz de inicio de sesión de WeChat. Luego, solicite el openid, la clave de sesión y otra información del usuario del servidor WeChat a través del código.

Nota: Cada código solo se puede usar una vez y es válido por 5 minutos. Por lo tanto, cuando se utiliza el código para iniciar sesión, es necesario convertirlo a tiempo en openid y session_key del usuario y otra información para evitar la caducidad del código.

3. Implementación del código

1. Agregue el archivo WxMaProperties en el directorio de configuración.

package com.ruoyi.xyhj.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

import java.util.List;

/**
 * @author <a href="https://github.com/binarywang">Binary Wang</a>
 */
@Data
@ConfigurationProperties(prefix = "wx.miniapp")
public class WxMaProperties {
    
    

    private List<Config> configs;

    @Data
    public static class Config {
    
    
        /**
         * 设置微信小程序的appid
         */
        private String appid;

        /**
         * 设置微信小程序的Secret
         */
        private String secret;

        /**
         * 设置微信小程序消息服务器配置的token
         */
        private String token;

        /**
         * 设置微信小程序消息服务器配置的EncodingAESKey
         */
        private String aesKey;

        /**
         * 消息格式,XML或者JSON
         */
        private String msgDataFormat;
    }

}

2. Inyecte wxMaService

package com.ruoyi.xyhj.config;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage;
import cn.binarywang.wx.miniapp.bean.WxMaSubscribeMessage;
import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
import cn.binarywang.wx.miniapp.message.WxMaMessageHandler;
import cn.binarywang.wx.miniapp.message.WxMaMessageRouter;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.error.WxRuntimeException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.File;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author <a href="https://github.com/binarywang">Binary Wang</a>
 */
@Slf4j
@Configuration
@EnableConfigurationProperties(WxMaProperties.class)
public class WxMaConfiguration {
    
    
    private final WxMaProperties properties;

    @Autowired
    public WxMaConfiguration(WxMaProperties properties) {
    
    
        this.properties = properties;
    }

    @Bean("wxMaService")
    public WxMaService wxMaService() {
    
    
        List<WxMaProperties.Config> configs = this.properties.getConfigs();
        if (configs == null) {
    
    
            throw new WxRuntimeException("大哥,拜托先看下项目首页的说明(readme文件),添加下相关配置,注意别配错了!");
        }
        WxMaService maService = new WxMaServiceImpl();
        maService.setMultiConfigs(
            configs.stream()
                .map(a -> {
    
    
                    WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();
//                WxMaDefaultConfigImpl config = new WxMaRedisConfigImpl(new JedisPool());
                    // 使用上面的配置时,需要同时引入jedis-lock的依赖,否则会报类无法找到的异常
                    config.setAppid(a.getAppid());
                    config.setSecret(a.getSecret());
                    config.setToken(a.getToken());
                    config.setAesKey(a.getAesKey());
                    config.setMsgDataFormat(a.getMsgDataFormat());
                    return config;
                }).collect(Collectors.toMap(WxMaDefaultConfigImpl::getAppid, a -> a, (o, n) -> o)));
        return maService;
    }

    @Bean
    public WxMaMessageRouter wxMaMessageRouter(WxMaService wxMaService) {
    
    
        final WxMaMessageRouter router = new WxMaMessageRouter(wxMaService);
        router
            .rule().handler(logHandler).next()
            .rule().async(false).content("订阅消息").handler(subscribeMsgHandler).end()
            .rule().async(false).content("文本").handler(textHandler).end()
            .rule().async(false).content("图片").handler(picHandler).end()
            .rule().async(false).content("二维码").handler(qrcodeHandler).end();
        return router;
    }

    private final WxMaMessageHandler subscribeMsgHandler = (wxMessage, context, service, sessionManager) -> {
    
    
        service.getMsgService().sendSubscribeMsg(WxMaSubscribeMessage.builder()
            .templateId("此处更换为自己的模板id")
            .data(Lists.newArrayList(
                new WxMaSubscribeMessage.MsgData("keyword1", "339208499")))
            .toUser(wxMessage.getFromUser())
            .build());
        return null;
    };

    private final WxMaMessageHandler logHandler = (wxMessage, context, service, sessionManager) -> {
    
    
        log.info("收到消息:" + wxMessage.toString());
        service.getMsgService().sendKefuMsg(WxMaKefuMessage.newTextBuilder().content("收到信息为:" + wxMessage.toJson())
            .toUser(wxMessage.getFromUser()).build());
        return null;
    };

    private final WxMaMessageHandler textHandler = (wxMessage, context, service, sessionManager) -> {
    
    
        service.getMsgService().sendKefuMsg(WxMaKefuMessage.newTextBuilder().content("回复文本消息")
            .toUser(wxMessage.getFromUser()).build());
        return null;
    };

    private final WxMaMessageHandler picHandler = (wxMessage, context, service, sessionManager) -> {
    
    
        try {
    
    
            WxMediaUploadResult uploadResult = service.getMediaService()
                .uploadMedia("image", "png",
                    ClassLoader.getSystemResourceAsStream("tmp.png"));
            service.getMsgService().sendKefuMsg(
                WxMaKefuMessage
                    .newImageBuilder()
                    .mediaId(uploadResult.getMediaId())
                    .toUser(wxMessage.getFromUser())
                    .build());
        } catch (WxErrorException e) {
    
    
            e.printStackTrace();
        }

        return null;
    };

    private final WxMaMessageHandler qrcodeHandler = (wxMessage, context, service, sessionManager) -> {
    
    
        try {
    
    
            final File file = service.getQrcodeService().createQrcode("123", 430);
            WxMediaUploadResult uploadResult = service.getMediaService().uploadMedia("image", file);
            service.getMsgService().sendKefuMsg(
                WxMaKefuMessage
                    .newImageBuilder()
                    .mediaId(uploadResult.getMediaId())
                    .toUser(wxMessage.getFromUser())
                    .build());
        } catch (WxErrorException e) {
    
    
            e.printStackTrace();
        }

        return null;
    };

}

3. Llamar

	@Resource
    private WxMaService wxMaService;

    public String wxLoginOrRegister(String code) {
    
    
        if (StringUtils.isBlank(code)) {
    
    
            return "empty jscode";
        }
        try {
    
    
            //根据code获取openid
            WxMaJscode2SessionResult session = wxMaService.getUserService().getSessionInfo(code);
        } catch (WxErrorException e) {
    
    
          throw new WxLoginException(e.getMessage());
        }finally {
    
    
            WxMaConfigHolder.remove();//清理ThreadLocal
        }
    }

Para obtener más información, consulte el ejemplo oficial: https://github.com/binarywang/weixin-java-miniapp-demo o javadoc

4. Mini programa de pago WeChat

1.Diagrama de flujo empresarial

Insertar descripción de la imagen aquí

2. Instrucciones de cifrado y descifrado de firma, clave privada, certificado e información confidencial

1. Formato de firma
Esperamos que los desarrolladores técnicos del comerciante construyan la cadena de firma de acuerdo con las reglas acordadas en el documento actual. WeChat Pay utilizará el mismo método para construir la cadena de firma. Si el comerciante construye la cadena de firma de manera incorrecta, la verificación de la firma fallará. El formato específico de la cadena de firma se explicará a continuación.

Hay cinco líneas en la cadena de firma y cada línea tiene un parámetro. Termina con \n (carácter de nueva línea, valor de codificación ASCII 0x0A), incluida la última línea. Si el parámetro en sí termina con \n, también es necesario agregar \n.

HTTP请求方法\n
URL\n
请求时间戳\n
请求随机串\n
请求报文主体\n

Dirección del documento:
https://pay.weixin.qq.com/docs/merchant/development/interface-rules/signature-generación.html

2. Clave privada
Cuando un comerciante solicita un certificado API de comerciante, la herramienta de certificación generará la clave privada del comerciante y la guardará en el archivo apiclient_key.pem en la carpeta del certificado local. La clave privada también se puede exportar desde el certificado p12 del comerciante a través de herramientas. Guarde correctamente el archivo de clave privada del comerciante.
Dirección del documento:
https://pay.weixin.qq.com/docs/merchant/development/interface-rules/privatekey-and-certificate.html

3. Certificado
El certificado API del comerciante se refiere al certificado aplicado por el comerciante y contiene el número de comerciante, el nombre de la empresa y la información de la clave pública del comerciante.

WeChat Payment APIv3 utiliza un certificado emitido por una Autoridad de certificación (CA). Los comerciantes pueden generar cadenas de solicitud de certificado por sí mismos o descargar la herramienta de certificado de pago WeChat para generar cadenas de solicitud de certificado. Después de enviar la cadena de solicitud de certificado a la plataforma comercial, puede obtener el archivo de certificado API del comerciante. Preste atención para guardar el archivo de clave privada de forma segura.

Cada certificado de la plataforma de pago WeChat es válido por 5 años. Antes de que caduque el certificado, WeChat Pay utilizará gradualmente el nuevo certificado de la plataforma para generar firmas. Para evitar fallas en la verificación de firmas debido a la falta de los certificados correspondientes, el sistema comercial debe admitir múltiples certificados de la plataforma de pago WeChat y descargar periódicamente nuevos certificados a través de la interfaz e implementarlos en el servidor. Consulte nuestras pautas de renovación de certificados para evitar depender de la renovación manual de certificados y garantizar el funcionamiento continuo de su negocio.

Dirección del documento:
https://pay.weixin.qq.com/docs/merchant/development/interface-rules/privatekey-and-certificate.html

4. Cifrado y descifrado
Para garantizar la confidencialidad de los campos de información confidencial (como la dirección del usuario, el número de tarjeta bancaria, el número de teléfono móvil, etc.) durante el proceso de comunicación, WeChat Payment API v3 requiere que los comerciantes cifren la información confidencial enviada. campos. En consecuencia, WeChat Pay cifrará los campos de información confidencial posteriores y los comerciantes deberán descifrarlos antes de poder obtener el texto original. A continuación se describen en detalle los métodos de cifrado y descifrado y cómo realizar los cálculos correspondientes.

Dirección del documento:
https://pay.weixin.qq.com/docs/merchant/development/interface-rules/SENSITIVE-data-encryption.html

3. Implementación del código

1. Agregue el archivo WxPayProperties en el directorio de configuración.

package com.ruoyi.xyhj.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 * wxpay pay properties.
 *
 * @author Binary Wang
 */
@Data
@ConfigurationProperties(prefix = "wx.pay")
public class WxPayProperties {
    
    
  /**
   * 设置微信公众号或者小程序等的appid
   */
  private String appId;

  /**
   * 微信支付商户号
   */
  private String mchId;

  /**
   * 微信支付商户V3密钥
   */
  private String apiV3Key;

  /**
   * 证书号
   */
  private String certSerialNo;

  /**
   * apiclient_key.pem证书文件的绝对路径或者以classpath:开头的类路径
   */
  private String privateKeyPath;

  /**
   * apiclient_cert.pem证书文件的绝对路径或者以classpath:开头的类路径
   */
  private String privateCertPath;

  /**
   * 回调地址
   */
  private String notifyUrl;

}

2. Inyecte WxPayService

package com.ruoyi.xyhj.config;

import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author Binary Wang
 */
@Configuration
@ConditionalOnClass(WxPayService.class)
@EnableConfigurationProperties(WxPayProperties.class)
@AllArgsConstructor
public class WxPayConfiguration {
    
    
  private WxPayProperties properties;

  @Bean("wxPayService")
  @ConditionalOnMissingBean
  public WxPayService wxService() {
    
    
    WxPayConfig payConfig = new WxPayConfig();
    payConfig.setAppId(StringUtils.trimToNull(this.properties.getAppId()));
    payConfig.setMchId(StringUtils.trimToNull(this.properties.getMchId()));
    payConfig.setApiV3Key(StringUtils.trimToNull(this.properties.getApiV3Key()));
    payConfig.setCertSerialNo(StringUtils.trimToNull(this.properties.getCertSerialNo()));
    payConfig.setPrivateKeyPath(StringUtils.trimToNull(this.properties.getPrivateKeyPath()));
    payConfig.setPrivateCertPath(StringUtils.trimToNull(this.properties.getPrivateCertPath()));
    payConfig.setNotifyUrl(StringUtils.trimToNull(this.properties.getNotifyUrl()));
    payConfig.setTradeType("JSAPI");
    payConfig.setSignType("MD5");
    WxPayService wxPayService = new WxPayServiceImpl();
    wxPayService.setConfig(payConfig);
    return wxPayService;
  }

}

3. Cree una orden de prepago

Tenga en cuenta que estamos usando la versión v3, por lo que al crear un objeto o llamar a wxpayService, debe llamar a la versión v3; de lo contrario, es posible que los parámetros no coincidan.

/**
* 创建预支付订单
*/
    @Override
    @Transactional
    public WxUnifiedOrderVo createOrder(PatriarchCreateOrderBo bo) throws WxPayException, IOException, SignatureException, NoSuchAlgorithmException, InvalidKeyException {
    
    
  
        //构建预支付订单对象
        WxPayUnifiedOrderV3Request orderV3Request = buildWxPayUnifiedOrderRequest(order, parentUser.getOpenid(), product.getName());
        WxPayUnifiedOrderV3Result wxPayUnifiedOrderV3Result = wxPayService.unifiedOrderV3(TradeTypeEnum.JSAPI, orderV3Request);
        //构建返回参数
        WxUnifiedOrderVo tokenJSAPI = WechatSignUtil.getTokenJSAPI(wxPayProperties.getAppId(), wxPayUnifiedOrderV3Result.getPrepayId(), wxPayProperties.getPrivateKeyPath());
        return tokenJSAPI;
    }
/**
     * 构建统一下单对象
     * @param order 订单对象
     * @param openId user openId
     * @param productName 产品名
     * @return
     */
    public WxPayUnifiedOrderV3Request buildWxPayUnifiedOrderRequest(TelOrder order, String openId,String productName){
    
    
        WxPayUnifiedOrderV3Request orderRequest = new WxPayUnifiedOrderV3Request();
        //设置订单号
        orderRequest.setOutTradeNo(order.getId().toString());
        //设置交易结束时间为24小时
        orderRequest.setTimeExpire(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX").format(new Date()));
        //设置订单金额
        orderRequest.setAmount(new WxPayUnifiedOrderV3Request.Amount()
                        .setTotal(BaseWxPayRequest.yuanToFen(order.getAmount().toString())));
        //设置支付者信息
        orderRequest.setPayer(new WxPayUnifiedOrderV3Request.Payer().setOpenid(openId));
        //设置商品描述
        orderRequest.setDescription(productName);
        return orderRequest;
    }

4. Clase de herramienta de firma

package com.ruoyi.xyhj.utils;

import com.ruoyi.xyhj.domain.vo.WxUnifiedOrderVo;

import java.io.IOException;
import java.io.InputStream;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.UUID;

public class WechatSignUtil {
    
    

    /**
     * 参考网站 https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_4.shtml
     * 计算签名值
     *
     * @param appId
     * @param prepay_id
     * @return
     * @throws IOException
     * @throws SignatureException
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     */
    public static WxUnifiedOrderVo getTokenJSAPI(String appId, String prepay_id, String privateKey) throws IOException, SignatureException, NoSuchAlgorithmException, InvalidKeyException {
    
    
        // 获取随机字符串
        String nonceStr = getNonceStr();
        // 获取微信小程序支付package
        String packagestr = "prepay_id=" + prepay_id;
        long timestamp = System.currentTimeMillis() / 1000;
        //签名,使用字段appId、timeStamp、nonceStr、package计算得出的签名值
        String message = buildMessageTwo(appId, timestamp, nonceStr, packagestr);
        //获取对应的签名
        String signature = sign(message.getBytes("utf-8"),privateKey);
        // 组装返回
        WxUnifiedOrderVo vo = new WxUnifiedOrderVo();
        vo.setAppId(appId);
        vo.setTimeStamp(String.valueOf(timestamp));
        vo.setNonceStr(nonceStr);
        vo.setPackageStr(packagestr);
        vo.setSignType("RSA");
        vo.setPaySign(signature);
        return vo;
    }

    /**
     * 生成随机数
     * @return
     */
    public static String getNonceStr(){
    
    
        return UUID.randomUUID().toString()
                .replaceAll("-", "")
                .substring(0, 32);
    }
    /**
     * 拼接参数
     *
     * @return
     */

    public static String buildMessageTwo(String appId, long timestamp, String nonceStr, String packag) {
    
    
        return appId + "\n"
                + timestamp + "\n"
                + nonceStr + "\n"
                + packag + "\n";
    }
    /**
     * 生成签名
     *
     * @return
     */
    public static String sign(byte[] message,String privateKey) throws NoSuchAlgorithmException, SignatureException, IOException, InvalidKeyException {
    
    
        Signature sign = Signature.getInstance("SHA256withRSA"); //SHA256withRSA
        sign.initSign(getPrivateKey(privateKey));
        sign.update(message);
        return Base64.getEncoder().encodeToString(sign.sign());
    }
    /**
     * 获取私钥
     * @param filename 私钥文件路径  (required)
     * @return 私钥对象
     */
    public static PrivateKey getPrivateKey(String filename) throws IOException {
    
    
        System.out.println("filename:" + filename);
        filename = filename.replace("classpath:", "");
        WechatSignUtil wechatSignUtil = new WechatSignUtil();
        InputStream resourceAsStream = wechatSignUtil.getClass().getClassLoader().getResourceAsStream(filename);
        byte[] bytes = new byte[0];
        bytes = new byte[resourceAsStream.available()];
        resourceAsStream.read(bytes);
        String content = new String(bytes);
//        String content = new String(Files.readAllBytes(Paths.get(resource.getPath())), "utf-8");
        try {
    
    
            String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
                    .replace("-----END PRIVATE KEY-----", "")
                    .replaceAll("\\s+", "");

            KeyFactory kf = KeyFactory.getInstance("RSA");
            return kf.generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));
        } catch (NoSuchAlgorithmException e) {
    
    
            throw new RuntimeException("当前Java环境不支持RSA", e);
        } catch (InvalidKeySpecException e) {
    
    
            throw new RuntimeException("无效的密钥格式");
        }
    }
}

5. Interfaz de devolución de llamada

 @ApiOperation(value = "支付回调通知处理")
    @PostMapping("/wx/notify/order")
    public void parseOrderNotifyResult(@RequestBody String resultData) throws WxPayException {
    
    
        WxPayOrderNotifyV3Result notifyV3Result = wxPayService.parseOrderNotifyV3Result(resultData,null);
        log.info("回调:{}",notifyV3Result.getResult());
        orderService.wxNotify(notifyV3Result.getResult());
    }

Supongo que te gusta

Origin blog.csdn.net/qq_43548590/article/details/132837104
Recomendado
Clasificación