You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java/Spring接收Viber REST API回调问题排查与求助

解决Viber公共账号机器人在Spring中接收用户消息的问题

看起来你已经迈出了不错的第一步——成功部署到Heroku并通过Postman设置了Webhook,但用户消息没触发日志,核心问题在于当前的控制器没有正确匹配Viber的回调请求格式,也没处理它发送的事件 payload。我来帮你理清Viber的回调机制,并给出可运行的修正方案。

先搞懂Viber的回调逻辑

Viber的机器人回调是POST请求:当用户发送消息、关注机器人或产生其他交互事件时,Viber服务器会向你设置的Webhook URL发送两个关键内容:

  • X-Viber-Content-Signature请求头(用来验证请求是否来自合法的Viber服务器)
  • JSON格式的请求体,包含事件类型、用户信息、消息内容等核心数据

你的当前代码只处理了请求头,既没指定POST方法,也没接收请求体,而且@RequestMapping默认匹配所有路径和方法,可能和其他路由冲突,这就是用户消息没触发日志的直接原因。

分步解决问题

1. 创建Viber事件的实体类

首先需要定义对应Viber回调JSON的Java实体,让Spring能自动解析请求体。这里给出包含核心字段的示例:

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

@JsonIgnoreProperties(ignoreUnknown = true)
public class ViberEvent {
    @JsonProperty("event")
    private String eventType; // 比如 "message", "subscribed" 等事件类型

    @JsonProperty("sender")
    private ViberUser sender;

    @JsonProperty("message")
    private ViberMessage message;

    // getter和setter方法
    public String getEventType() { return eventType; }
    public void setEventType(String eventType) { this.eventType = eventType; }
    public ViberUser getSender() { return sender; }
    public void setSender(ViberUser sender) { this.sender = sender; }
    public ViberMessage getMessage() { return message; }
    public void setMessage(ViberMessage message) { this.message = message; }

    @JsonIgnoreProperties(ignoreUnknown = true)
    public static class ViberUser {
        @JsonProperty("id")
        private String userId;
        @JsonProperty("name")
        private String userName;

        // getter和setter方法
        public String getUserId() { return userId; }
        public void setUserId(String userId) { this.userId = userId; }
        public String getUserName() { return userName; }
        public void setUserName(String userName) { this.userName = userName; }
    }

    @JsonIgnoreProperties(ignoreUnknown = true)
    public static class ViberMessage {
        @JsonProperty("text")
        private String text;
        @JsonProperty("type")
        private String messageType; // 比如 "text"

        // getter和setter方法
        public String getText() { return text; }
        public void setText(String text) { this.text = text; }
        public String getMessageType() { return messageType; }
        public void setMessageType(String messageType) { this.messageType = messageType; }
    }
}

2. 修正Spring控制器

更新你的控制器,指定POST方法、匹配Webhook的路径,并同时接收签名头和请求体:

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@RestController
public class DemoApplication {

    // 从环境变量读取Token,不要硬编码!Heroku可通过配置变量设置
    private static final String VIBER_AUTH_TOKEN = System.getenv("VIBER_AUTH_TOKEN");

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    // 路径要和你设置Webhook时的URL完全一致,比如你设的是https://xxx.herokuapp.com/viber/webhook,就写"/viber/webhook"
    @PostMapping("/viber/webhook")
    public String handleViberCallback(
            @RequestHeader("X-Viber-Content-Signature") String serverSideSignature,
            @RequestBody String requestBody) throws Exception {

        // 先验证签名合法性
        if (!ViberSignatureValidator.isValidSignature(requestBody, serverSideSignature, VIBER_AUTH_TOKEN)) {
            System.out.println("收到无效的Viber请求:签名验证失败");
            return "Invalid Signature";
        }

        // 解析请求体为ViberEvent对象
        ObjectMapper objectMapper = new ObjectMapper();
        ViberEvent viberEvent = objectMapper.readValue(requestBody, ViberEvent.class);

        // 打印事件日志
        System.out.println("===== 收到Viber事件 =====");
        System.out.println("事件类型: " + viberEvent.getEventType());
        System.out.println("发送用户ID: " + viberEvent.getSender().getUserId());
        System.out.println("发送用户昵称: " + viberEvent.getSender().getUserName());
        
        if ("message".equals(viberEvent.getEventType())) {
            System.out.println("消息内容: " + viberEvent.getMessage().getText());
        }

        // 必须返回200 OK的响应,Viber才会认为回调成功
        return "OK";
    }
}

3. 实现签名验证(关键步骤)

Viber要求你验证请求签名,确保请求来自官方服务器。签名是用机器人的Authentication Token和请求体做HMAC-SHA256加密后生成的Base64字符串,工具方法如下:

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;

public class ViberSignatureValidator {
    private static final String HMAC_SHA256 = "HmacSHA256";

    public static boolean isValidSignature(String requestBody, String signatureHeader, String authToken) {
        try {
            SecretKeySpec secretKey = new SecretKeySpec(authToken.getBytes(), HMAC_SHA256);
            Mac mac = Mac.getInstance(HMAC_SHA256);
            mac.init(secretKey);
            byte[] hashBytes = mac.doFinal(requestBody.getBytes());
            String computedSignature = Base64.getEncoder().encodeToString(hashBytes);
            return computedSignature.equals(signatureHeader);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
}

4. 确认Webhook设置正确

  • 确保设置Webhook时的URL和控制器@PostMapping的路径完全一致(比如https://your-heroku-app-name.herokuapp.com/viber/webhook)
  • 重新设置Webhook:用Postman发送POST请求到https://chatapi.viber.com/pa/set_webhook,请求体带上你的Auth Token和正确的Webhook URL
  • 用heroku logs --tail命令查看Heroku实时日志,观察用户发送消息时是否有请求到达

调试小技巧

  • 用Heroku实时日志功能(heroku logs --tail)追踪请求状态
  • 如果仍无请求,检查Viber开发者后台的Webhook状态,查看是否有错误提示
  • 确认Heroku应用处于运行状态(用heroku ps命令查看)

内容的提问来源于stack exchange,提问作者Mykhailo Bormashenko

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:13:48