Stripe Webhook Verification Error with Play Framework - scala

I am trying to set up stripe payment with a play framework application. I am having issues with setting up the webhook.
com.stripe.exception.SignatureVerificationException: No signatures found matching the expected signature for payload
This is the error I keep getting when I try and construct the event sent from stripe. I print out the values for the body and the signature and they look like they should be correct. This is the code I am using to collect the 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
}
}
}

Karllekko was on the right track. Play framework automatically parsed it as a json which caused the error. request.body.asText didn't work because the content-type header value was set to json. Tolarant Text would have worked except for stripe sends their webhooks with utf-8 and tolarant text doesn't parse with utf-8. So I ended up having to use a RawBuffer and turning that into a String (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)
...
...

You need to pass the raw body of the request exactly as received to constructEvent, not the JSON parsed version.
I believe that should be
event = Webhook.constructEvent(request.body.asText, sig, "whsec_5XwS8yCNOcq1CKfhh2Dtvm8RaoaE3p7b")
in this case?
You can have a look at the examples from Stripe as well[0]. But the core issue is that you must sign the exact string sent by Stripe, without any interference by your code or a framework in the middle.
[0]- https://github.com/stripe/stripe-java/blob/master/src/test/java/com/stripe/net/WebhookTest.java

zamsler's solution works for me. Thank you! Saved me a lot of time. For anyone who wants the Play 2.8 Java version:
#BodyParser.Of(BodyParser.Raw.class)
public Result webhook(Http.Request request) {
String payload = request.body().asBytes().utf8String();
String sigHeader = request.getHeaders().get("Stripe-Signature").get();
String endpointSecret = "xxxxxxxx";
try {
event = Webhook.constructEvent(payload, sigHeader, endpointSecret);
} catch (SignatureVerificationException e) {
e.printStackTrace();
// Invalid signature
return badRequest();
}
...
...
}

Related

Check string in gatling simulation

Trying to check if a string exists on the body. Similar to checking status which is .check(status.is(200)). I want to check for string as well. tried .check(bodyString.is("greeting")) but got an error:
val scn = scenario("GreetingPages")
.during(testTimeSecs) {
exec(
http ("greeting")
.get("/greeting")
.check(status.is(200))
.check(bodyString.is("Greeting"))
).pause(minWaitMs,maxWaitMs)
.exec(
http("greeting1")
.get("/greeting1")
.check(status.is(200))
.check(bodyString.is("Greeting1"))
).pause(minWaitMs,maxWaitMs)
.exec(
http("Third page")
.get("/greeting2")
.check(status.is(200))
.check(bodyString.is("Greeting2"))
).pause(minWaitMs,maxWaitMs)
}
---- Errors --------------------------------------------------------------------
bodyString.find.is(Greeting), but actually found {"message":"G 9 (47.37%)
reeting"}
bodyString.find.is(Greeting1), but actually found {"message":" 5 (26.32%)
Greeting1"}
bodyString.find.is(Greeting2), but actually found {"message":" 5 (26.32%)
Greeting2"}
The reason is the bodyString return the full response body, as it described in the documentation.
You could use in matcher (you can see the implementation here - look for InMatcher[A], but it won't work because you need to switch expected.contains(actualValue) with expected.contains(expected)
I suggest you implement your own MyOwnInMatcher:
class MyOwnInMatcher[A](expected: Seq[A]) extends Matcher[A] {
def name: String = "customIn"
protected def doMatch(actual: Option[A]): Validation[Option[A]] = actual match {
case Some(actualValue) =>
if (expected.contains(actualValue))
actual.success
else
s"found $actualValue".failure
case _ => Validator.FoundNothingFailure
}
}
and use it:
.check(jsonPath("$.message").validate(customIn("Greeting")))), which checks if "Greeting" exists in the message attribute of the json response body.

Custom spray.io directive to validate request header value

I am new to spray and I am trying to write a custom directive. I would like the directive to reject the request if the header value is not valid otherwise leave the request alone.
I've tried to absorb this page:
http://spray.io/documentation/1.1.2/spray-routing/key-concepts/directives/
Specifically, the part about the responder chain. I'm trying to create something at the level of the bar Directive in the illustration. I'm just not getting how to pass the context unchanged to the inner route.
My else block below is not correct but expresses what I am trying to do. I just can't figure out how to implement it.
Any help would be greatly appreciated.
trait ApiKeyDirective {
import spray.routing.directives.HeaderDirectives._
import spray.routing.directives.BasicDirectives._
def validateApiKey(): Directive1 = {
headerValueByName("api-key") {key =>
val valid = key == "123"
if (!valid) reject() else pass
}
}
}
object ApiKeyDirective extends ApiKeyDirective
You can combine
headerValueByName:
def headerValueByName(headerName: String): Directive1[String]
with validate:
def validate(check: ⇒ Boolean, errorMsg: String): Directive0
For example:
def validateApiKey(route: Route) =
headerValueByName("api-key") { key =>
validate(key == "123", "Invalid API key") {
route
}
}
or without validate:
def validateApiKey(route: Route) =
headerValueByName("api-key") { key =>
if (key == "123")
route
else
reject(ValidationRejection("Invalid API key"))
}
Usage:
lazy val route = ...
... ~
pathPrefix("test_directive") {
get {
validateApiKey {
complete("ok")
}
}
} ~
...
Test from cmd/shell:
# curl http://localhost:8080/test_directive
Request is missing required HTTP header 'api-key'
# curl http://localhost:8080/test_directive -H 'api-key: bad'
Invalid API key
# curl http://localhost:8080/test_directive -H 'api-key: 123'
"ok"
I'm just not getting how to pass the context unchanged to the inner
route.
Spray does that for you!
Your code is mostly correct, there are just 2 simple problems to fix!
Firstly, you need to flatMap headerValueByName("api-key") directive.
Secondly, the return type will be Directive0 because the directive won't provide any value.
So final code would look like this:
object ApiKeyDirective {
import spray.routing.Directives._
val validateApiKey: Directive0 =
headerValueByName("api-key").flatMap { key =>
val valid = key == "123"
if (!valid) reject() else pass
}
}
Also, I recommend you to add a custom rejection to reject() block so that API users will be informed when their api key is invalid.

Dynamically constructing closures from a map?

I'm trying to make a general method for sending SOAP requests and getting responses. I'm programming using Groovy and I'm using the wslite library to help me out with SOAP. Here's a sample snippet for making a SOAP request and getting a response:
#Grab('com.github.groovy-wslite:groovy-wslite:1.1.2')
import wslite.soap.*
SOAPClient client = new SOAPClient('http://www.dneonline.com/calculator.asmx')
def response = client.send(SOAPAction: 'http://tempuri.org/Add') {
body {
Add(xmlns: 'http://tempuri.org/') {
intA(x)
intB(y)
}
}
}
By general, I meant being able to dynamically create a SOAP request (given certain information such as the service/method name, the parameters contained in the method, etc.) and obtain the SOAP response. I'm thinking something like this:
#Grab('com.github.groovy-wslite:groovy-wslite:1.1.2')
import wslite.soap.*
def getResponse(String clientURL, String action, String service, String serviceNamespace, Map parameters, ...) {
SOAPClient client = new SOAPClient(clientURL)
def response = client.send(SOAPAction: action) {
body {
"$service"(xmlns: serviceNameSpace) {
...
}
}
}
}
My problem lies in constructing the closure for the request body. Like, in example, if my method received a service Add, a serviceNamespace http://tempuri.org/, and a parameter map like so: [intA: x, intB: y]... how do I merge all of these so that I can construct this kind of closure:
Add(xmlns: 'http://tempuri.org/') {
intA(x)
intB(y)
}
I'm pretty much a newbie to Groovy, so don't be too harsh. If there's a better way to implement this concept of a general method, I would gladly like to hear it. The concept is similar to this. But I'd rather play with Map than a String. I'm not using Grails, really. Just plain Groovy.
In short, cfrick is correct:
[intA: x, intB: y].each{fn,arg -> delegate."$fn"(arg) }
How does it work?
An easy way to see how this works is to simulate it with a fake client class:
groovy.util.NodeBuilder
class Client {
def send(String action, Closure closure) {
closure.delegate = new NodeBuilder()
closure()
}
}
def client = new Client()
def response = client.send('http://tempuri.org/Add') {
body {
Add(xmlns: 'http://tempuri.org/') {
intA(1)
intB(2)
}
}
}
assert response.Add[0].#xmlns == 'http://tempuri.org/'
assert response.Add.intA.text() == '1'
assert response.Add.intB.text() == '2'
In the example above, the response object is created by Groovy's NodeBuilder. It's just a quick way to prototype something that processes the closure passed to Client.send().
With this testable code I'll try what cfrick suggested and validate that it works:
def doIt(String action, String service, String serviceNamespace, Map params) {
def client = new Client()
client.send(action) {
body {
"$service"(xmlns: serviceNamespace) {
params.each { method, argument ->
delegate."$method"(argument)
}
}
}
}
}
response = doIt('http://tempuri.org/Add', 'Add', 'http://tempuri.org/', [intA: 1, intB: 2])
assert response.Add[0].#xmlns == 'http://tempuri.org/'
assert response.Add.intA.text() == '1'
assert response.Add.intB.text() == '2'
Request Body
In addition, you can factor out the process of creating the request body:
def getRequestBody(String service, String serviceNamespace, Map params) {
{ ->
"$service"(xmlns: serviceNamespace) {
params.each { method, argument ->
delegate."$method"(argument)
}
}
}
}
def doIt(String action, String service, String serviceNamespace, Map params) {
def client = new Client()
client.send(action) {
body(getRequestBody(service, serviceNamespace, params))
}
}

Play 2.2.1 Scala - Redirect for 413 REQUEST_ENTITY_TOO_LARGE

I have the following Controller action as form post resolver:
def importCompletionsSubmit(indexName: String) = AuthenticatedAction {
Action.async {
implicit request => {
completionsForm.bindFromRequest().fold(
errors => Future.successful(Ok(html.crudindex.importCompletionsForm(indexName, errors))),
completions => {
val autoCompletionService = new AutoCompletionService(new Elasticsearch)
autoCompletionService.importCompletions(indexName, completions.text) map {
result: BulkImportResult =>
if (result.error) Redirect(routes.ListIndices.index(Option.empty[String])).flashing("error" -> Messages("error.bulkItemsFailed", result.failures))
else Redirect(routes.ListIndices.index(Option.empty[String])).flashing("success" -> Messages("success.completionsAdded", result.requests))
}
}
)
}
}
}
I know that I can change the max length value for this action but what I would like to do is sending the user back to the form with a nice error message when he enters too much text.
If the request body is exceeding the default max length I get a completly blank page and only the browser console shows "413 (Request Entity Too Large)". I tried to catch this error in my global object but that did not change anything. It seems to me that the global onError trigger is not entered when a parser sends back an errorpage. Still a blank page. I also tried to catch that error inside the action but it seems to me that the action code is not entered because the body parser is already throwing this blank error page.
Is there a way to send the user back to the form action when the body exceeds the max length?
Something like this should work for you:
def test = Action.async(parse.maxLength(1024, parse.multipartFormData)) { implicit request =>
Future(request.body match {
case Left(MaxSizeExceeded(length)) => Ok(your_pretty_error_page.scala.html)
case Right(body) => {
...
}
})
}

Accessing session information inside a custom PartHandler in Play! Framework 2.0

In order to achieve a streaming upload I have written a custom PartHandler (Thread here ).
I now need to access a value that is stored inside the play! session inside the PartHandler.
How can I do that ?
Code sample:
def uploadFile() =
Action( parse.multipartFormData(myPartHandler) )
{ request =>
request.session.get("myValue") // <-- Recovering value is fine here
Ok("Done") }
def myPartHandler: BodyParsers.parse.Multipart.PartHandler[MultipartFormData.FilePart[Result]] = {
parse.Multipart.handleFilePart {
case parse.Multipart.FileInfo(partName, filename, contentType) =>
// ** Need to access a value in session here **
//request.session.get("myValue")...
// Handle part ...
Thanks!
With the help of other users of the play! framework google group, here is how to access the request inside a custom partHandler.
//Create a body parser
val myBodyParser = BodyParser { request =>
parse.multipartFormData(myPartHandler(request)).apply(request)
}
def uploadFile() = Action(myBodyParser)
{request =>Ok("Done")}
def myPartHandler(request: RequestHeader) : BodyParsers.parse.Multipart.PartHandler[MultipartFormData.FilePart[Result]] = {
parse.Multipart.handleFilePart {
case parse.Multipart.FileInfo(partName, filename, contentType) =>
println(request.session.get("myValueKey"));