macrozheng/mall-swarm · warning

支付回调签名校验失败!

Error message

支付回调签名校验失败!

What it means

AlipayServiceImpl.notify calls AlipaySignature.rsaCheckV1 to verify the signature of Alipay's async payment callback using the configured Alipay public key, charset, and sign type. If verification returns false (or throws), the callback is rejected with this warning and 'failure' is returned, causing the order to never be marked paid and Alipay to retry the notification.

Solutions

  1. Verify alipayConfig.getAlipayPublicKey() is Alipay's platform public key (from the open platform app details), not your own application private/public key.
  2. Confirm charset and signType in alipayConfig exactly match those used in the Alipay app settings (e.g., UTF-8, RSA2).
  3. Ensure the notify handler receives ALL raw callback parameters unmodified (don't re-encode, reorder, or drop fields before verification).
  4. Check that the callback URL belongs to the same Alipay app/gateway environment whose key is configured.
  5. If the same endpoint serves sandbox traffic, use the sandbox Alipay public key accordingly.

Example fix

// before (config)
alipay.public-key=MIIBIjANBgkq...your-app-public-key...
// after (config) - use the Alipay PLATFORM public key from open.alipay.com app settings
alipay.public-key=MIIBIjANBgkq...alipay-platform-public-key...
alipay.sign-type=RSA2
alipay.charset=UTF-8
Defensive patterns

Strategy: validation

Validate before calling

// Before deploying, self-test key configuration
boolean ok;
try {
    ok = AlipaySignature.rsaCheckV1(params, alipayConfig.getAlipayPublicKey(),
            alipayConfig.getCharset(), alipayConfig.getSignType());
} catch (AlipayApiException e) {
    log.error("签名校验异常", e);
    ok = false;
}
if (!ok) { log.warn("拒绝回调: 签名不匹配"); return "failure"; }

Type guard

boolean isConfigValid(AlipayConfig c) {
    return c != null
        && StringUtils.isNotBlank(c.getAlipayPublicKey())
        && StringUtils.isNotBlank(c.getCharset())
        && StringUtils.isNotBlank(c.getSignType());
}

Try / catch

try {
    signVerified = AlipaySignature.rsaCheckV1(params, alipayConfig.getAlipayPublicKey(),
            alipayConfig.getCharset(), alipayConfig.getSignType());
} catch (AlipayApiException e) {
    log.error("支付回调签名校验异常!", e);
    return "failure";
}

Prevention

When it happens

Trigger: A callback arrives whose params fail rsaCheckV1: wrong/placeholder alipayPublicKey (e.g., merchant app public key instead of Alipay's public key), mismatched charset or signType, params altered/missing fields (e.g., sign or sign_type stripped), or a forged/unauthorized callback request.

Common situations: Swapping the app's own public key with Alipay's platform public key in config; switching Alipay apps/keys between environments; sign_type mismatch after upgrading to RSA2; a proxy or gateway re-encoding the callback and corrupting parameters; attackers probing the notify URL.

Related errors


AI-assisted analysis of macrozheng/mall-swarm@04c442fe31 (2026-09-08). Data as JSON: /api/errors/62714a4d30822a47. Report an issue: GitHub.

Appendix: source

Thrown at mall-portal/src/main/java/com/macro/mall/portal/service/impl/AlipayServiceImpl.java:93

        try {
            //调用SDK验证签名
            signVerified = AlipaySignature.rsaCheckV1(params, alipayConfig.getAlipayPublicKey(), alipayConfig.getCharset(), alipayConfig.getSignType());
        } catch (AlipayApiException e) {
            log.error("支付回调签名校验异常!",e);
            e.printStackTrace();
        }
        if (signVerified) {
            String tradeStatus = params.get("trade_status");
            if("TRADE_SUCCESS".equals(tradeStatus)){
                result = "success";
                log.info("notify方法被调用了,tradeStatus:{}",tradeStatus);
                String outTradeNo = params.get("out_trade_no");
                portalOrderService.paySuccessByOrderSn(outTradeNo,1);
            }else{
                log.warn("订单未支付成功,trade_status:{}",tradeStatus);
            }
        } else {
            log.warn("支付回调签名校验失败!");
        }
        return result;
    }

    @Override
    public String query(String outTradeNo, String tradeNo) {
        AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
        //******必传参数******
        JSONObject bizContent = new JSONObject();
        //设置查询参数,out_trade_no和trade_no至少传一个
        if(StrUtil.isNotEmpty(outTradeNo)){
            bizContent.put("out_trade_no",outTradeNo);
        }
        if(StrUtil.isNotEmpty(tradeNo)){
            bizContent.put("trade_no",tradeNo);
        }
        //交易结算信息: trade_settle_info
        String[] queryOptions = {"trade_settle_info"};

View on GitHub (pinned to 04c442fe31)