Documentation
Test environmentLive environmentStatusContact us
  • Overview
    • Get started
    • Resource guides
      • For business users
      • For finance teams
      • For integration teams
    • Stripe onboarding
    • Live use cases
      • Usage overview
  • Start using the dashboard
    • Access
    • Payment services
    • Collaborators and access control
    • One-time payment links
      • Single link creation
      • Bulk link creation
      • Send payment links
      • Payment link page
      • Payment receipt email
    • Subscriptions
      • Set-up
      • View subscriptions
      • Manage subscriptions
    • Payments and refunds
      • Payment methods
      • Dashboard
      • Refunds
    • Payouts
    • Reports
    • Payment workflows with Plumber
      • Set-up
      • Testing
    • Payment forms
      • Set-up
      • Testing
      • Resources
  • Start an API integration
    • Get started
    • Endpoints
    • Integrating with your e-services
    • Errors
    • Idempotency
    • Pagination
    • Go-live checklist
  • API RESOURCES
    • Events
      • The event object
      • Types of events
    • Payments
      • Create a payment
      • Retrieve payments
      • Send email for payment
      • Cancel a payment
      • Refund a payment
      • Get all payments from payment service
  • WEBHOOKS
    • Introduction
    • Set-up
      • Events
      • Best practices
  • FAQ
    • Access
    • Costs
    • Timeline
    • API keys
    • API integration
    • Payments
    • Payment methods
    • Payouts and transaction fees
    • Refunds
    • Security and compliance
    • Downtime and maintenance
  • Policies
    • Privacy Policy
    • Terms of Use
Powered by GitBook
On this page

Was this helpful?

  1. WEBHOOKS
  2. Set-up

Best practices

Review these best practices to make sure your webhooks remain secure and function well with your integration

PreviousEventsNextAccess

Last updated 1 month ago

Was this helpful?

Verify events are sent from PaySG

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:

  1. Extract the signature from the HTTP header called PaySG-Signature

  2. The value of the header has the format t=timestamp, v1=signature. Extract the timestamp and the signature

  3. Concatenate the timestamp and the JSON-stringified HTTP payload, separated by a . to form the signature payload

  4. Use HMAC-SHA256 and the symmetric key provided to you by PaySG to verify that the signature is valid

Duplicate event handling

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.

Specifically listening to event types your integration requires

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.

Exempt webhook route from CSRF protection

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.

Preventing replay attacks

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.

Verifying signatures manually

You can also create a custom solution by following these steps:

Step 1: Extract the timestamp and signatures from the header

PaySG-Signature:
t=, // timestamp
v1= // event-id

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.

cross-site request forgery
replay attack
downgrade attacks
HMAC
SHA-256