概要 #
Verify the events that Wooshpay sends to your webhook endpoints.
Wooshpay signature header #
について Wooshpay-Signature
header included in each signed event contains a timestamp and signature.
The timestamp is prefixed by t=
, and each signature is prefixed by v1
.
For example:
Wooshpay-Signature:t=1492774577,v1=6fdfb9c357542b8ee07277f5fca2c6f728bae2dce9be2f91412f4de922c1bae4
Webhook secret key #
Wooshpay generates a unique secret key for each webhook. whsec_
Before verify signatures, you need to retrieve your webhook secret from webhook.secret
in webhook object.
Verifying signatures #
By comparing the the signature in the header to the expected signature, You can verify that the even were sent by Wooshpay, not by a third party.
To verify the signature step by step, you can follow the guide below:
Step 1: Extract the timestamp and signatures from the header #
Split the header, using the ,
character as the separator, to get a list of elements. Then split each element, using the =
character as the separator, to get a prefix and value pair.
The value for the prefix t
corresponds to the timestamp, and v1
corresponds to the signature. You can discard all other elements.
For example:
t=1687845304,v1=6fdfb9c357542b8ee07277f5fca2c6f728bae2dce9be2f91412f4de922c1bae4
Step 2: Prepare the signed_payload
string #
について signed_payload
string is created by concatenating:
- The timestamp (as a string)
- The character
.
- The actual JSON payload (the request body)
For example:
1687845304
+.
+JSON payload
1687845304.{
"id": "evt_1NNUrjL6kclEVx6Mb1x5dKJ3",
"object": "event",
"api_version": "2022-11-15",
"created": 1687845303,
"data": {
"object": {
"id": "prod_O9oUVgsSaordCT",
"object": "product",
"active": true,
"livemode": true,
"name": "test",
"type": "service",
"livemode": true,
"pending_webhooks": 1,
"type": "product.created"
}
Step 3: Determine the expected signature #
Compute a HMAC with the SHA256 hash function.
- Use the endpoint’s webhook secret as the key, which you can get it from
webhook.secret
in webhook object - Use the
signed_payload
string as the message which you already prepared in step 2.
Through the key + message, you can compute a HMAC string, which is expected signature
For example:
public static void main(String[] args) {
String webhookSecret = "whsec_261V2mfsXt1BsOjJbHaQOxnTzhWZKrUE";
String timestamp = "1687845304";
String requestBody = "{\"id\":\"evt_1NNUrjL6kclEVx6Mb1x5dKJ3\",\"object\":\"event\",\"api_version\":\"2022-11-15\",\"created\":1687845303,\"data\":{\"object\":{\"id\":\"prod_O9oUVgsSaordCT\",\"object\":\"product\",\"active\":true,\"livemode\":true,\"name\":\"test\",\"type\":\"service\",\"livemode\":true,\"pending_webhooks\":1,\"type\":\"product.created\"}";
String signedPayload = timestamp+"."+requestBody;
String signature = hmacSha256(webhookSecret,signedPayload);
String WooshpaySignature = "t="+timestamp+",v1="+signature;
}
/**
* HMAC-SHA256
*/
public static String hmacSha256(String secret, String message) {
String res;
try {
Mac mac = Mac.getInstance("HmacSHA256");
SecretKey secretKey = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
mac.init(secretKey);
byte[] hash = mac.doFinal(message.getBytes());
res = Hex.encodeHexString(hash);
} catch (Exception e) {
return null;
}
return res;
}
Step 4: Compare the signatures #
Compare the signature in the header to the expected signature.
For an equality match, compute the difference between the current timestamp and the received timestamp, then decide if the difference is within your tolerance.
To protect against timing attacks, you can use a constant-time string comparison to compare the expected signature to each of the received signatures.