Documentation
Ask or search…
K
Links

Webhooks

Leverage incoming webhooks to receive real-time updates from PaySG. Listen for events from your payment service so that your integration can automatically trigger reactions.

Why use webhooks?

Webhooks are a useful tool when building PaySG integrations, as they allow your e-services to receive events in real-time as they occur in your payment service.
To enable webhook events, you will need to register webhook endpoints. Once registered, PaySG can push real-time event data to your application's webhook endpoint whenever events happen in your PaySG account. PaySG uses HTTPS to send webhook events to your app as a JSON payload that includes an Event object.
Receiving webhook events is particularly useful for listening to asynchronous events. The current version of webhooks supports events for when a payer has paid.

Event Overview

PaySG generates event data that you can use to inform you of activity on your payment service. When an event occurs, PaySG generates a new Event object. For example, when a payer pays on PaySG, you receive payment.succeeded event.
By registering webhook endpoints in your payment service, you enable PaySG to automatically send Event objects as part of POST requests to the registered webhook endpoint hosted by your application.

Event Object

The Event object we send to your webhook endpoint provides a snapshot of the object that changed.
Currently, we only support the payment.succeeded event type.

Example event payload

{
"id": "evt_752ced1f-00ce-4905-8c46-367371dd78e6",
"data": {
"object": {
"id": "wLAnFWQLYyYNKBimc2ZkA",
"dueDate": null,
"metadata": {},
"payoutId": null,
"createdAt": "2023-07-17T17:15:12.200+08:00",
"creatorId": "user_655d26a2-ddfc-4718-a008-bd181c1522e8",
"payerName": "John Doe",
"updatedAt": "2023-10-02T16:17:08.167+08:00",
"payerEmail": "[email protected]",
"paymentUrl": "http://pay.gov.sg/payments/payment01",
"description": "Payment for XXX",
"referenceId": "INV001",
"latestStatus": "paid",
"payerAddress": "36 Robinson Road, Singapore 068877",
"refundStatus": "not_refunded",
"amountInCents": 12.34,
"paymentStatus": "paid",
"payerIdentifier": "S1234567A",
"paidOutTimestamp": null,
"paymentServiceId": "payment_service_fd724c82-786b-43ba-b7d7-1eecab0388e1",
"emailDeliveryStatus": "sent",
"paymentSentTimestamp": "2023-07-31T10:01:48.761+08:00",
"stripePaymentIntentId": "pi_3NUnWOLRKRlnGcim1Nzy18Z0",
"paymentCancelledTimestamp": null,
"paymentSucceededTimestamp": "2023-10-02T16:17:08.164+08:00",
"paymentFullyRefundedTimestamp": null
}
},
"type": "payment.succeeded",
"object": "event",
"created_at": "2023-10-02T16:17:08.210+08:00",
"payment_service_id": "payment_service_fd724c82-786b-43ba-b7d7-1eecab0388e1"
}

Why event object gets generated

This table describes different scenarios that trigger generating events.
Source
Trigger
PaySG platform
When a user action (i.e. pays) in the PaySG platform results in an API call

How to set up your webhook integration

To begin receiving webhook events in your application, you can create and register a webhook endpoint by following these steps:
  1. 1.
    Identify the specific events that you want to monitor.
  2. 2.
    Develop a webhook endpoint function that can receive event data POST requests on your e-service.
  3. 3.
    Register your endpoint with the PaySG team. For now, this can be done by contacting the team and providing the following fields for them.
    • webhook_url the endpoint of your e-service that PaySG should send webhooks to.
    • description optional field for you to describe the webhook.
    • subscribed_events list of subscribed events from PaySG that your e-service would like to subscribe to.
  4. 4.
    Ensure that your webhook endpoint is secure to prevent unauthorized access.

Identify the events to monitor

Use the PaySG API reference to identify the PaySG events and the Event objects your webhook endpoint service needs to parse.

Create a webhook endpoint function

To set up a webhook endpoint function that can accept webhook requests with a POST method, you can follow these steps:
  1. 1.
    Create an HTTPS endpoint function that can handle POST requests with a JSON payload consisting of an event object. Webhooks will only be supported for HTTPS endpoints.
  2. 2.
    Ensure that your endpoint function quickly returns a successful status code (2xx) prior to any complex logic that could cause a timeout. For example, you must return a 2xx response before updating your internal system records.

Register and manage your webhook in PaySG

After testing your webhook endpoint function, you can register the endpoint's accessible URL. Currently, you can do so by contacting the PaySG team to create the webhook for you. This will allow PaySG to know where to deliver events.
{
webhook_url: https://<agency-website>/<your-webhook-endpoint> // e-service endpoint
description: 'Description of the webhook' // optional
subscribed_events: ['payment.succeeded'] // array of events e-service subscribes to
}
There are no current limits to how many webhooks endpoints you can register with PaySG. Registered webhook endpoints must be publicly accessible HTTPS URLs. To register your webhook endpoint, provide the publicly accessible HTTPS URL to your endpoint, and a list of events that you will be receiving in your endpoint along with an optional description.

Webhook URL format

The URL format to register a webhook endpoint is:
https://<agency-website>/<your-webhook-endpoint>
For example, if your domain is https://agency-eservice.com and the route to your webhook endpoint is @app.route('/paysg_webhooks', methods=['POST']), specify https://agency-eservice.com/paysg_webhooks as the endpoint URL.

Add a webhook endpoint

  1. 1.
    Open the Settings tab of your PaySG payment service
  2. 2.
    Click Add endpoint
  3. 3.
    Add your webhook endpoint’s HTTPS URL in Endpoint URL.
  4. 4.
    Optionally, include a description for your webhook
  5. 5.
    Select the event types you wish to receive in your local webhook endpoint in Select events.
  6. 6.
    Click Add endpoint.

Manage a webhook endpoint configuration

To update or delete existing webhook endpoints, contact the PaySG team to do it for you.

Secure your webhooks

After confirming that your webhook endpoint connection works as expected, secure the connection by implementing webhook best practices.
One especially important best practice is to use webhook signatures to verify that PaySG generated a webhook request and that it did not come from a server acting like PaySG.

Event delivery behaviours

This section provides documentation on the different behaviors that you can expect when PaySG sends events to your webhook endpoint. Specifically, it covers event retry deliveries and event ordering.

Retry behaviour

PaySG attempts to deliver an event to your webhook endpoint for up to 3 days with an exponential back off.
If your endpoint has been deleted when PaySG attempts a retry, future retries of that event will be prevented.

Event ordering

PaySG doesn’t guarantee delivery of events in the order in which they’re generated.
Your endpoint should not expect delivery of events in order, and needs to handle out-of-order delivery accordingly.

Best practices for using webhooks

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

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 form 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 cross-site request forgery 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.

Verify events are sent from PaySG

You should verify webhook signatures 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. This allows you to verify that the events were sent by PaySG, not by a third party.
To verify webhook signatures, you can follow these steps:
  1. 1.
    Verify the signature using the webhook signature the PaySG team provided you

Retrieving your endpoint's secret

For now, they can be obtained through direct request to the PaySG Team. The PaySG team will provide the secret to you when we successfully help you register it.
The secret is a unique value that is used to sign webhook events sent to your endpoint. You will need to use this secret to verify the signature of incoming webhook events.

Preventing replay attacks

To prevent replay attack, 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
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 downgrade attacks, ignore all schemes that are not v1.
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
Compute an HMAC with the SHA-256 hash function. Use the endpoint's signing secret as the key, and use the signed_payload string as the message.
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.