原因:小程序和APP、公众号等支付方式夸端口调用支付,后台配置多个appId时
A程序中的openid 在B程序中支付。即使用A程序的openid和B程序的appIdy去调用wxpay.unifiedOrder(data)
把请求统一支付的参数输出:得到当前的appid,微信返回后看到另一个Appid,如果两个一致,则不会出现不匹配问题。不一致,就会报appid 与 openId 不配的错误。
解决方式:由于系统中的WeiXinConfigUtil文件实现了微信SDK的WXPayConfig,多个appid在上送请求支付时对获取APPid做了区分获取,但是没有重写getAppID()方法,导致默认使用appID作为属性的参数值是固定的,在SDK内部获取appid时,不是动态获取,而是配置好的固定值,所以重写getAppID()方法,通过tradeType来区分获取的是哪一个appid
package com.wlnl.lanaer.service.api.util; import cn.hutool.core.io.IoUtil; import cn.hutool.core.util.StrUtil; import com.github.wxpay.sdk.WXPayConfig; import lombok.extern.slf4j.Slf4j; import org.springframework.core.io.ClassPathResource; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; /** * <p> * 微信支付工具类 * </p> * * @author luolei * @Version: V1.0 * @since 2019-06-04 21:02 */ @Slf4j public class WeiXinConfigUtil implements WXPayConfig { private byte[] certData; /** * 微信支付应用ID app_id */ public static final String APP_ID = "123456789"; /** * 微信支付key */ public static final String KEY = "666666666666"; /** * 微信支付商户号 mch_id */ public static final String MCH_ID = "8888888888"; public WeiXinConfigUtil() { this.certData = getCertStream("apiclient_cert.p12"); } /** * 读取resource目录的配置文件 * * @param path : 文件名称 * @return 返回读取byte数组 */ public byte[] getCertStream(String path) { try { if (StrUtil.isEmpty(path)) { throw new Exception("读取文件路径为空"); } ClassPathResource classPathResource = new ClassPathResource(path); //获取文件流 InputStream stream = classPathResource.getInputStream(); byte[] content = IoUtil.readBytes(stream); stream.read(content); stream.close(); return content; } catch (IOException e) { log.error("读取文件流异常: {}", e.getMessage()); e.printStackTrace(); } catch (Exception e) { log.error("读取文件路径异常: {}", e.getMessage()); e.printStackTrace(); } return null; } @Override public String getAppID() { return APP_ID; } //parnerid,商户号 @Override public String getMchID() { return MCH_ID; } @Override public String getKey() { return KEY; } @Override public InputStream getCertStream() { ByteArrayInputStream certBis = new ByteArrayInputStream(this.certData); return certBis; } @Override public int getHttpConnectTimeoutMs() { return 8000; } @Override public int getHttpReadTimeoutMs() { return 10000; } }
如果微信支付key和微信支付商户号 mch_id不需要修该时(即多个程序使用的微信支付key和微信支付商户号 mch_id都是同一个)
我们只需要修改微信程序的app_id,这是就可以再创建一个类WeiXinMiniProConfigUtil继承我们上面写的WeiXinConfigUti类,并且重写getAppID()方法,代码如下
package com.wlnl.lanaer.service.api.util; /** * @author luolei * @version v1.0.1 * @since 2021/9/7 **/public class WeiXinMiniProConfigUtil extends WeiXinConfigUtil { /** * 小程序appid * 如果有多个appId的情况不能写死 */ // public static final String WX_MINI_PRO_APP_ID = "123456789"; //老版小程序appId public static String WX_MINI_PRO_APP_ID = "987654321"; /** * 注意这里一定要重写getAppID()方法,返回我们指定的appId * @return */ @Override public String getAppID() { return WX_MINI_PRO_APP_ID; } public WeiXinMiniProConfigUtil(){} /** * 使用可修改的appId,防止appId与openId不匹配的问题 * @param appId */ public WeiXinMiniProConfigUtil(String appId) { WX_MINI_PRO_APP_ID = appId; } }
下面就是测试微信支付是否能下单成功了,代码如下
package com.wlnl.lanaer.service.api.mq; import com.github.wxpay.sdk.WXPay; import com.github.wxpay.sdk.WXPayUtil; import com.google.common.collect.Maps; import com.wlnl.lanaer.service.api.KxkdApiServiceApplication; import com.wlnl.lanaer.service.api.constant.enums.ResultEnum; import com.wlnl.lanaer.service.api.exeception.LanaerException; import com.wlnl.lanaer.service.api.exeception.OrderExistException; import com.wlnl.lanaer.service.api.util.WeiXinMiniProConfigUtil; import com.wlnl.lanaer.service.api.vendors.tencent.pay.WeiXinPayConstant; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.util.StringUtils; import java.util.Map; /** * @Description TODO * @Date 2023/04/08 15:27 * @Created by luolie */ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = {KxkdApiServiceApplication.class}) @AutoConfigureMockMvc @ExtendWith(SpringExtension.class) @ActiveProfiles(profiles = "dev") public class TestWxPush { /** * 微信下单测试 */ @Test public void doUnifiedOrderTest() { Map map = doUnifiedOrder("5555555555", "111111111111111111"); } /** * 小程序支付场景 */ public Map doUnifiedOrder(String appId, String openId) { try { WeiXinMiniProConfigUtil config = new WeiXinMiniProConfigUtil(appId); //使用前端传过来的appId,防止appId与openId不匹配的问题 WXPay wxpay = new WXPay(config); Map<String, String> data = Maps.newHashMap(); data.put("appid", config.getAppID()); data.put("mch_id", config.getMchID()); data.put("nonce_str", WXPayUtil.generateNonceStr()); data.put("body", "测试微信支付"); data.put("out_trade_no", "2021WERUN1647840687637"); data.put("total_fee", "1000"); // 支付金额 data.put("spbill_create_ip", "59.37.125.120"); //自己的服务器IP地址 data.put("notify_url", "支付成功后的回调url(外网可访问的https协议的url)"); // 异步通知地址(请注意必须是外网) data.put("trade_type", "JSAPI"); data.put("openid", openId); // trade_type是JSAPI的时候,必须有openid data.put("sign", WXPayUtil.generateSignature(data, config.getKey())); //使用官方API请求预付订单 Map<String, String> response = wxpay.unifiedOrder(data); //主要返回以下5个参数 if (WeiXinPayConstant.WEIXIN_PAY_RESULT_SUCCESS.equals(response.get(WeiXinPayConstant.WEIXIN_PARY_RETURN_CODE)) && WeiXinPayConstant.WEIXIN_PAY_RESULT_SUCCESS.equals(response.get(WeiXinPayConstant.WEIXIN_RESULT_CODE))) { Map<String, String> param = Maps.newHashMap(); param.put("appId", config.getAppID()); param.put("timeStamp", System.currentTimeMillis() / 1000 + ""); param.put("nonceStr", WXPayUtil.generateNonceStr()); param.put("package", "prepay_id=" + response.get("prepay_id")); param.put("signType", "MD5"); param.put("sign", WXPayUtil.generateSignature(param, config.getKey())); // 以下是返回的 param.put("partnerid", response.get("mch_id")); param.put("prepayid", response.get("prepay_id")); param.put("body", "测试微信支付"); param.put("out_trade_no", "2021WERUN1647840687637"); param.put("total_fee", "1000"); return param; } else { String err_code = response.get("err_code"); // 商户订单号重复 if (!StringUtils.isEmpty(err_code) && "INVALID_REQUEST".equals(err_code)) { throw new OrderExistException(response.get(WeiXinPayConstant.WEIXIN_ERR_CODE_DESG)); } // 订单创建失败 if (WeiXinPayConstant.WEIXIN_PAY_RESULT_FALL.equals(response.get(WeiXinPayConstant.WEIXIN_PARY_RETURN_CODE))) { throw new LanaerException(response.get(WeiXinPayConstant.WEIXIN_PARY_RETURN_MSG)); } String err_code_des = response.get("err_code_des"); if (StringUtils.hasLength(err_code_des)) { throw new LanaerException(err_code_des); } } } catch (OrderExistException e) { throw e; } catch (Exception e) { e.printStackTrace(); throw new LanaerException(ResultEnum.ERROR_CODE.getCode(), "下单失败"); } throw new LanaerException(ResultEnum.ERROR_CODE.getCode(), "下单失败"); } }