PAYATHON 2026

使用 alipay-easysdk 时支付宝支付二维码无法显示

支付小周

结论

alipay-easysdk 的当面付预创建接口返回的是二维码内容字符串,也就是 qr_code,并非二维码图片。浏览器直接输出该字符串时,不会自动显示二维码。需要使用 ZXing 或前端二维码组件,将字符串编码为图片。

如果 qr_code 为空,问题不在图片显示环节。应先查看支付宝接口返回的 code、sub_code 和 sub_msg,再检查应用配置、签名密钥、支付产品权限,以及生产环境和沙箱环境是否匹配。

正确的处理流程

先调用当面付预创建接口:

AlipayTradePrecreateResponse response =
        Factory.Payment.FaceToFace().preCreate(
                "测试商品",
                "ORDER_202609090001",
                "0.01"
        );

接口调用成功后,获取二维码内容:

String qrContent = response.getQrCode();

qrContent 通常是需要写入二维码的地址。它不是图片 URL,不能直接放进 <img src="">。正确的流程是:

调用 preCreate
    ↓
检查支付宝返回码
    ↓
取得 response.getQrCode()
    ↓
使用二维码库生成 PNG/SVG
    ↓
返回给浏览器显示

先确认支付宝接口调用成功

仅检查 response 是否为 null 不够,还需要记录完整的错误信息:

AlipayTradePrecreateResponse response =
        Factory.Payment.FaceToFace().preCreate(
                "测试商品",
                orderNo,
                "0.01"
        );

if (response == null) {
    throw new IllegalStateException("支付宝接口未返回结果");
}

if (!"10000".equals(response.getCode())) {
    throw new IllegalStateException(
            "支付宝下单失败,code=" + response.getCode()
                    + ", msg=" + response.getMsg()
                    + ", subCode=" + response.getSubCode()
                    + ", subMsg=" + response.getSubMsg()
    );
}

String qrContent = response.getQrCode();

if (qrContent == null || qrContent.trim().isEmpty()) {
    throw new IllegalStateException("支付宝接口调用成功,但未返回 qr_code");
}

确认 code 为 10000,并且 qrContent 非空后,再生成二维码图片。

使用 ZXing 生成二维码图片

项目需要引入 ZXing 的 core 和 javase 模块。具体版本应结合项目使用的 JDK、Spring Boot 版本和现有依赖管理来确定。排查二维码问题时,没有必要反复更换 alipay-easysdk 版本。

二维码生成方法如下:

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;

import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

public class QrCodeUtils {

    public static byte[] createPng(String content, int width, int height)
            throws Exception {

        Map<EncodeHintType, Object> hints = new HashMap<>();
        hints.put(EncodeHintType.CHARACTER_SET, StandardCharsets.UTF_8.name());
        hints.put(EncodeHintType.MARGIN, 1);

        BitMatrix matrix = new QRCodeWriter().encode(
                content,
                BarcodeFormat.QR_CODE,
                width,
                height,
                hints
        );

        try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
            MatrixToImageWriter.writeToStream(matrix, "PNG", output);
            return output.toByteArray();
        }
    }
}

在 Spring Boot 中,可以直接返回 PNG 图片:

import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.payment.facetoface.models.AlipayTradePrecreateResponse;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.math.BigDecimal;
import java.util.UUID;

@RestController
public class AlipayController {

    @GetMapping(
            value = "/alipay/qr",
            produces = MediaType.IMAGE_PNG_VALUE
    )
    public ResponseEntity<byte[]> createQrCode() throws Exception {
        String orderNo = UUID.randomUUID().toString().replace("-", "");
        String totalAmount = new BigDecimal("0.01").toPlainString();

        AlipayTradePrecreateResponse response =
                Factory.Payment.FaceToFace().preCreate(
                        "测试商品",
                        orderNo,
                        totalAmount
                );

        if (!"10000".equals(response.getCode())) {
            throw new IllegalStateException(
                    "支付宝下单失败:"
                            + response.getSubCode()
                            + " - "
                            + response.getSubMsg()
            );
        }

        String qrContent = response.getQrCode();

        if (qrContent == null || qrContent.trim().isEmpty()) {
            throw new IllegalStateException("支付宝未返回二维码内容");
        }

        byte[] image = QrCodeUtils.createPng(qrContent, 300, 300);

        return ResponseEntity.ok()
                .contentType(MediaType.IMAGE_PNG)
                .body(image);
    }
}

浏览器访问 /alipay/qr 后,会直接收到一张 PNG 图片。

如果前后端分离

后端也可以只返回 qrCode 字符串:

return Map.of(
        "orderNo", orderNo,
        "qrCode", response.getQrCode()
);

前端拿到字符串后,再通过二维码组件生成图片。下面这种写法通常无法正确显示:

<img src="支付宝返回的 qr_code">

原因是 qr_code 表示二维码需要承载的内容,并不是浏览器可以直接加载的图片地址。

检查 EasySDK 初始化配置

如果 response.getQrCode() 为空或接口报错,需要确认 SDK 已在调用支付接口前完成初始化:

import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.kernel.Config;

Config config = new Config();
config.protocol = "https";
config.gatewayHost = "openapi.alipay.com";
config.signType = "RSA2";
config.appId = "你的应用 AppId";
config.merchantPrivateKey = "应用私钥";
config.alipayPublicKey = "支付宝公钥";

Factory.setOptions(config);

检查以下配置:

  • merchantPrivateKey 应填写应用私钥,不能填写支付宝公钥。
  • alipayPublicKey 应填写支付宝公钥,不能填写应用公钥。
  • 确认密钥内容没有被换行、空格或配置文件转义破坏。
  • 沙箱应用、沙箱账号和沙箱网关需要配套使用。
  • 生产应用不能使用沙箱网关或沙箱密钥。
  • 应用需要开通并具备相应的当面付能力。
  • 如果使用证书模式,应按证书模式配置,不能与普通公钥模式混用。

不要在公开日志中打印私钥、完整接口响应或用户付款地址。

确认调用了正确的接口

如果需要在商家页面展示收款二维码,应调用当面付预创建接口:

Factory.Payment.FaceToFace().preCreate(...)

如果调用的是电脑网站支付,例如:

Factory.Payment.Page().pay(...)

接口通常返回支付宝收银台需要的表单或页面内容,处理方式与当面付不同。此时应把返回的表单内容交给浏览器,不能通过读取 qrCode 自行生成图片。

仍然无法显示时的排查顺序

  1. 记录 code、msg、subCode 和 subMsg,确认支付宝接口是否调用成功。
  2. 检查 response.getQrCode() 是否为空。
  3. 如果不为空,通过日志或调试器确认字符串是否完整。
  4. 使用 ZXing 单独为固定文本生成二维码,确认图片输出接口正常。
  5. 检查 HTTP 响应的 Content-Type 是否为 image/png。
  6. 检查前端是否把 JSON 响应当成图片,或者误将 qrCode 放进 <img src="">。
  7. 确认订单号没有重复、金额格式正确,并查看支付宝返回的具体业务错误。
  8. 最后检查应用权限、网关、AppId 和密钥配置。

反复切换 alipay-easysdk 版本,通常无法解决二维码显示问题。排查时应先判断属于哪种情况:支付宝没有返回 qr_code,还是已经返回 qr_code,但应用没有将其生成二维码图片。

备注:内容仅供参考。