【问题标题】:Stripe Webhook Verification Error with Play FrameworkPlay 框架的 Stripe Webhook 验证错误
【发布时间】:2018-10-26 17:50:45
【问题描述】:

我正在尝试使用播放框架应用程序设置条带支付。我在设置 webhook 时遇到问题。

com.stripe.exception.SignatureVerificationException: No signatures found matching the expected signature for payload

这是我在尝试构建从条带发送的事件时不断遇到的错误。我打印出正文和签名的值,它们看起来应该是正确的。这是我用来收集 webhook 的代码。

 def webhook = Action { implicit request: Request[AnyContent] =>
println(request.headers.toMap)
val bodyOp:Option[JsValue] = request.body.asJson
val sigOp:Option[String] = request.headers.get("Stripe-Signature")
var event: Event = null
if (bodyOp.isEmpty || sigOp.isEmpty) {
  WebhookController.logger.write("EMPTY BODY OR SIG body-"+bodyOp+"   sig-"+sigOp,Logger.RED)
  BadRequest
} else {
  val body = bodyOp.get.toString
  val sig = sigOp.get
  println(body)
  println(sig)
  try {
    event = Webhook.constructEvent(body, sig, "whsec_5XwS8yCNOcq1CKfhh2Dtvm8RaoaE3p7b")
    val eventType: String = event.getType
    eventType match {
      case "customer.subscription.deleted" => deleteCustomer(event)
      case "invoice.payment.succeeded" => successPayment(event)
      case "invoice.payment.failed" => failedPayment(event)
      case _ => WebhookController.logger.write("UNKNOWN " + event, Logger.RED)
    }

    Ok("")
  } catch {
    case e: JsonSyntaxException =>
      e.printStackTrace()
      WebhookController.logger.write("ERROR" + e.getMessage +" "+exceptionToString(e), Logger.RED)
      BadRequest
    case e: SignatureVerificationException =>
      e.printStackTrace()
      WebhookController.logger.write("ERROR" + e.getMessage + " "+exceptionToString(e), Logger.RED)
      WebhookController.logger.write("SIG ERROR header-"+e.getSigHeader+" status code-"+e.getStatusCode,Logger.RED )
      BadRequest
  }
}
}

【问题讨论】:

    标签: scala playframework stripe-payments webhooks


    【解决方案1】:

    Karllekko 走在了正确的轨道上。 Play 框架自动将其解析为导致错误的 json。 request.body.asText 不起作用,因为 content-type 标头值设置为 json。除了使用 utf-8 发送 webhook 并且 tolarant 文本不使用 utf-8 解析之外,Tolarant Text 本来可以工作。所以我最终不得不使用 RawBuffer 并将其转换为字符串 (https://www.playframework.com/documentation/2.6.x/ScalaBodyParsers)

    class WebhookController @Inject()(parsers: PlayBodyParsers) extends Controller() {
    
    def webhook = Action(parsers.raw) { implicit request: Request[RawBuffer] =>
      val bodyOp =  request.body.asBytes()
      val sigOp:Option[String] = request.headers.get("Stripe-Signature")
      var event: Event = null
      if (bodyOp.isEmpty || sigOp.isEmpty) {
        BadRequest
      } else {
        val body = bodyOp.get.utf8String
        val sig = sigOp.get
        try {
          event = Webhook.constructEvent(body, sig, secret)
          ...
          ...
    

    【讨论】:

    • 这确实是唯一有效的答案,我不明白其他人是如何被赞成的。
    【解决方案2】:

    您需要将收到的请求的原始正文完全传递给constructEvent,而不是 JSON 解析版本。

    我认为应该是

    event = Webhook.constructEvent(request.body.asText, sig, "whsec_5XwS8yCNOcq1CKfhh2Dtvm8RaoaE3p7b")

    在这种情况下?

    您也可以查看 Stripe 的示例[0]。但核心问题是您必须对 Stripe 发送的确切字符串进行签名,而不受您的代码或中间框架的任何干扰。

    [0]-https://github.com/stripe/stripe-java/blob/master/src/test/java/com/stripe/net/WebhookTest.java

    【讨论】:

    • 这不起作用。 request.body.asText 返回一个 None
    • 啊,抱歉,我自己没用过 Play,所以我不能 100% 确定如何获取 Stripe 需要的原始文本 - 不过很高兴你知道了!
    猜你喜欢
    • 2021-01-14
    • 2014-03-16
    • 2021-09-18
    • 2021-11-28
    • 2011-11-27
    • 1970-01-01
    • 1970-01-01
    • 2019-05-11
    • 1970-01-01
    相关资源
    最近更新 更多