Thanks to csaunders for pointing me in the right direction.
I was using the default BodyParser AnyContent
that implicitly converts the response body to json when the Content-type of the request specifies 'application/json'.
I had to modify my controller object to specify the 'raw' BodyParser:
import play.api._
import play.api.libs.iteratee.Enumerator
import play.api.mvc.SimpleResult
import play.api.mvc.ResponseHeader
import play.api.libs.json._
import play.Application
import play.api.mvc._
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
import play.api.Logger
import javax.crypto.SecretKey
import org.apache.commons.codec.binary.Base64
object Purchase extends Controller {
val shopifyAppSecretKey = "11111111111111111111111111111111"
def processPurchase() = Action( parse.raw ) {request =>
val bodyRaw = request.body.asBytes(3000).getOrElse(Array[Byte]())
val calculatedHash = encodeByteArray(shopifyAppSecretKey, bodyRaw)
val shopifyHash = request.headers.get("X-Shopify-Hmac-Sha256").getOrElse("")
Logger.info("keys '" + shopifyHash + "' || '" + calculatedHash + "' " + calculatedHash.equals(shopifyHash))
val json: JsValue = Json.parse( new String(bodyRaw) )
Ok( "Ok" ).as(HTML)
}
def encodeByteArray(key: String , data: Array[Byte]): String = {
val sha256_HMAC = Mac.getInstance("HmacSHA256");
val secret_key = new SecretKeySpec(key.getBytes(), "HmacSHA256");
sha256_HMAC.init(secret_key);
return new String( Base64.encodeBase64( sha256_HMAC.doFinal( data ) ) ).trim
}
}
Using the 'raw' BodyParser means that you have to convert the byte array to a string yourself and then parse that string manually to get your json but thats no real problem.
Now all is working as expected.
Thanks,
Tim