Created
October 11, 2019 11:50
-
-
Save brunojppb/ad2a2ccdb4de121d87ee84a0e7e51ef9 to your computer and use it in GitHub Desktop.
Handling HelpScout webhooks in Play Framework and Scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* How to handle HelpScout webhooks using Play Framework & Scala (Without using the HelpScout library) | |
* See original docs: https://developer.helpscout.com/webhooks/ | |
*/ | |
/** On your controller. | |
* Map this function to a valid route | |
* and register your webhook using the HelpScout API | |
*/ | |
def postWebhook: Action[RawBuffer] = actionBuilder.async(parse.raw) { | |
implicit request => | |
request.body.asBytes() match { | |
case Some(rawBody) => | |
val decodedString = rawBody.decodeString(Charset.forName("UTF-8")) | |
val signature = request.headers.get("X-Helpscout-Signature").getOrElse(throw new IllegalArgumentException("Missing signature")) | |
val notificationEvent = request.headers.get("X-HelpScout-Event").getOrElse(throw new IllegalArgumentException("Missing event")) | |
HelpScoutAPI.parseNotification(body, signature) match { | |
case Some(json) => // Notification is legit | |
// Handle your payload here | |
Ok(Json.obj()) | |
case None => // Probably a fake request | |
Ok(Json.obj()) | |
} | |
case None => | |
Ok(Json.obj()) | |
} | |
} | |
/** on your HelpScout helper */ | |
object HelpScoutAPI { | |
private val webhookSecretKey = "YOUR_SECRET_HERE" | |
def parseNotification(payload: String, helpscoutSignature: String): Option[JsValue] = { | |
val isValid = validateNotification(payload, helpscoutSignature) | |
if (isValid) { | |
Some(Json.parse(payload)) | |
} else { | |
logger.info("Invalid notification") | |
None | |
} | |
} | |
/** | |
* Validate HelpScout payload using HMAC Signatures | |
* see: https://developer.helpscout.com/webhooks/#verifying | |
* */ | |
import javax.crypto.Mac | |
import javax.crypto.spec.SecretKeySpec | |
import org.apache.commons.codec.binary.Base64 | |
private def validateNotification(payload: String, signature: String): Boolean = { | |
val mac = Mac.getInstance("HmacSHA1") | |
mac.init(new SecretKeySpec(webhookSecretKey.getBytes("UTF-8"), "HmacSHA1")) | |
val digest = payload.getBytes("UTF-8") | |
val signedPayload = mac.doFinal(digest) | |
val computedSignature = new String(Base64.encodeBase64(signedPayload)).trim | |
computedSignature == signature | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment