Best practices
Review these best practices to make sure your webhooks remain secure and function well with your integration
Last updated
Was this helpful?
Review these best practices to make sure your webhooks remain secure and function well with your integration
Last updated
Was this helpful?
Once your webhook has been registered, the PaySG team will provide you with your endpoint's secret. The secret is a unique value that is used to sign webhook events sent to your endpoint. You should use this secret to verify the signature of incoming webhook events to confirm that received events are sent from PaySG. PaySG signs webhook events that are sent to your endpoints by including a signature in each event's PaySG-Signature
header.
To verify webhook signatures, developers can follow these steps:
Extract the signature from the HTTP header called PaySG-Signature
The value of the header has the format t=timestamp, v1=signature
. Extract the timestamp and the signature
Concatenate the timestamp and the JSON-stringified HTTP payload, separated by a .
to form the signature payload
Use HMAC-SHA256 and the symmetric key provided to you by PaySG to verify that the signature is valid
Webhook endpoints may receive the same event more than once, which can result in duplicated event receipts. To prevent this, it's important to make your event processing idempotent. One way to achieve this is by logging the events that you have already processed, and then not processing any events that have already been logged.
To optimize the performance of your webhook endpoints, it's recommended that you configure them to receive only the types of events that are required by your integration. Listening for extra events, or all events, can put undue strain on your server.
You can change the events that a webhook endpoint receives in the PaySG Dashboard.
For applications using Rails, Django, or another web framework, your site might automatically check that every POST request contains a CSRF token as a security feature to protect you and your users from attempts. However, this might also prevent your site from processing legitimate events. In such cases, you might need to exempt the webhooks route from CSRF protection.
To prevent , PaySG includes a timestamp in the PaySG-Signature header. This timestamp is part of the signed payload and is verified by the signature, so an attacker cannot change the timestamp without invalidating the signature. If the signature is valid but the timestamp is too old, your e-service should reject the payload.
You can consider having a tolerance between the timestamp included in the header and the current time.
Note that if PaySG retries an event (for example, if your endpoint previously replied with a non-2xx status code), a new signature and timestamp will be generated for the new delivery attempt.
You can also create a custom solution by following these steps:
Step 1: Extract the timestamp and signatures from the header
Step 2: Prepare the signed_payload string
The signed_payload
string is created by concatenating:
The timestamp (as a string) t
The character .
The actual JSON payload (i.e., the request body).
Step 3: Determine the expected signature
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, use a constant-time-string comparison to compare the expected signature to each of the received signatures.
The PaySG-Signature
header included in each signed event contains a timestamp and one or more signatures that you must verify. The value for the prefix t corresponds to the timestamp, and v1 corresponds to the signature (or signatures). To prevent , ignore all schemes that are not v1
.
Compute an with the hash function. Use the endpoint's signing secret as the key, and use the signed_payload
string as the message.