Handle Webhooks for Payment Link


PhonePe Payment Gateway uses Webhook (S2S Callbacks) to notify you about key events like payment completion or refund status. Here’s how it works:

  • You need to provide a Webhook URL (a specific endpoint on your server) where these updates will be sent.
  • To ensure secure communication, you should set up a username and password for authentication.
  • Configure Webhook: Follow these steps to set up a new webhook for receiving event notifications.
    • Log in to your PhonePe Business Dashboard.
    • Set the environment mode using the Test Mode toggle located on the dashboard.
      • For Sandbox (Testing): Ensure the toggle is switched ON.
      • For Production (Live): Ensure the toggle is switched OFF.
    • Navigate to Developer Settings from the side menu.
    • Select the Webhook tab and click the Create Webhook button.
    • In the configuration form, fill in the following details:
      • Webhook URL: Your server’s endpoint URL to receive notifications.
      • Username: Your authentication username.
      • Password: Your authentication password.
      • Description: A brief description for your reference.
    • From the list of active events, choose:
      • paylink.order.completed
      • paylink.order.failed
    • Click Create to save and complete the configuration.
    • Your webhook is now active.
  • Authorization.
    • Once configured, PhonePe Payment Gateway will send updates to your server using the provided username and password.
    • These credentials will be used to create an Authorization header in the webhook response using SHA256 (username:password) method.
  • Verification.
    • For the incoming request, extract the header Authorization, verify it with the one which you have shared with us and accept the response if the Username and password match.
    • If the hash matches the one sent by PhonePe Payment Gateway, the update is valid, and the response payload can be consumed.
    • If it doesn’t match, the response should be ignored.

ℹ️ Authorization Header!


PhonePe Payment Gateway includes the Authorization header in the following format:
Authorization: SHA256(username:password)

  • For the incoming request, extract the basic authorization header “Authorization”, verify it with the one which you have shared with us and accept the response if Username and password matches.
    • Use the “payload.state” Parameter: For payment status, rely only on the root-level “payload.state” field in the response
    • Avoid Strict Deserialization: Don’t use overly strict rules for processing the response
    • Use the “event” Parameter: Ignore the “type” parameter in the webhook response. Use the “event” parameter instead to identify the event type
    • Time Format: The expireAt and timestamp fields will be in epoch time
Response for Paylink Order Completed
{
    "event": "paylink.order.completed",
    "payload": {
        "orderId": "OMOxx",
        "merchantId": "merchantId",
        "merchantOrderId": "merchantOrderId",
        "state": "COMPLETED",
        "amount": 10000,
        "expireAt": 1291391291,
        "metaInfo": {
            "udf1": "",
            "udf2": "",
            "udf3": "",
            "udf4": ""
        },
        "paymentFlow": {
            "type": "PAYLINK",
            "paylinkUrl": "<paylinkurl>"
        },
        "paymentDetails": [
            {
                "paymentMode": "UPI_QR",
                "transactionId": "OM12334",
                "timestamp": 12121212,
                "amount": 10000,
                "state": "COMPLETED",
                "splitInstruments": [
                    {
                        "amount": 10000,
                        "rail": {
                            "type": "UPI",
                            "upiTransactionId": "upi12313",
                            "vpa": "12****78@ybl"
                        },
                        "instrument": {
                            "type": "ACCOUNT",
                            "accountType": "SAVINGS",
                            "accountNumber": "******1234"
                        }
                    }
                ]
            }
        ]
    }
}
Response for Paylink Order Failed
{
    "event": "paylink.order.failed",
    "payload": {
        "orderId": "OMOxx",
        "merchantId": "merchantId",
        "merchantOrderId": "merchantOrderId",
        "state": "FAILED",
        "amount": 10000,
        "expireAt": 1291391291,
        "metaInfo": {
            "udf1": "",
            "udf2": "",
            "udf3": "",
            "udf4": ""
        },
        "paymentFlow": {
            "type": "PAYLINK",
            "paylinkUrl": "<paylinkurl>"
        },
        "paymentDetails": [
            {
                "paymentMode": "UPI_COLLECT",
                "timestamp": 12121212,
                "amount": 10000,
                "transactionId": "OM12333",
                "state": "FAILED",
                "errorCode": "AUTHORIZATION_ERROR",
                "detailedErrorCode": "ZM",
                "splitInstruments": [
                    {
                        "amount": 10000,
                        "rail": {
                            "type": "UPI",
                            "upiTransactionId": "upi12313",
                            "vpa": "12****78@ybl"
                        },
                        "instrument": {
                            "type": "ACCOUNT",
                            "accountType": "SAVINGS",
                            "accountNumber": "******1234"
                        }
                    }
                ]
            }
        ]
    }
}

If you don’t receive the Webhook callback, you can use the Payment Link Status to manually check the payment status.

Now that you have learned how to verify the payment and what happens when the webhook fails, this concludes your website integration. The next step is to complete UAT testing and understand the process to go live.

Is this article helpful?