Configuring Webhooks
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.
Webhook Setup
- Configure Webhook.
- Production: You can configure the Webhook URL, username, and password directly on the PhonePe Business dashboard.
- Sandbox: You must reach out to our Integration Team to set this up. To do so,
- Click Help in the side panel of your dashboard.
- Select Integration and click Contact Us.
- Share the webhook callback URL while creating the ticket.
- 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.
Webhook Event Types
- Order Events:
checkout.order.completed: Sent when an order is successfully completedcheckout.order.failed: Sent when an order fails
- Refund Events:
pg.refund.completed: Sent when a refund is successfully processedpg.refund.failed: Sent when a refund processing fails
Important: PhonePe Payment Gateway includes the Authorization header in the following format:
Authorization: SHA256(username:password)
Callback Validation/Verification
- 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
Webhook Responses
Response for Order Completed
{
"event": "pg.order.completed",
"payload": {
"orderId": "OMOxx",
"merchantId": "merchantId",
"merchantOrderId": "merchantOrderId",
"state": "COMPLETED",
"amount": 10000,
"expireAt": 1291391291,
"metaInfo": {
"udf1": "",
"udf2": "",
"udf3": "",
"udf4": ""
},
"paymentDetails": [
{
"paymentMode": "UPI_QR",
"transactionId": "OM12334",
"timestamp": 1724866793837,
"amount": 10000,
"state": "COMPLETED",
"splitInstruments": [
{
"amount": 10000,
"rail": {
"type": "UPI",
"upiTransactionId": "upi12313",
"vpa": "abcd@ybl"
},
"instrument": {
"type": "ACCOUNT",
"accountType": "SAVINGS",
"accountNumber": "121212121212"
}
}
]
}
]
}
}Response for Order Failed
{
"event": "pg.order.failed",
"payload": {
"orderId": "OMOxx",
"merchantId": "merchantId",
"merchantOrderId": "merchantOrderId",
"state": "EXPIRED",
"amount": 10000,
"expireAt": 1724866793837,
"metaInfo": {
"udf1": "",
"udf2": "",
"udf3": "",
"udf4": ""
},
"paymentDetails": [
{
"paymentMode": "UPI_COLLECT",
"timestamp": 1724866793837,
"amount": 10000,
"transactionId": "OM12333",
"state": "FAILED",
"errorCode": "AUTHORIZATION_ERROR",
"detailedErrorCode": "ZM",
"splitInstruments": [
{
"amount": 10000,
"rail": {
"type": "UPI",
"upiTransactionId": "upi12313",
"vpa": "abcd@ybl"
},
"instrument": {
"type": "ACCOUNT",
"accountType": "SAVINGS",
"accountNumber": "121212121212"
}
}
]
}
]
}
}Response for Refund Completed
Case 1: Original source of transaction = UPI
{
"event": "pg.refund.completed",
"payload": {
"merchantId": "merchantId",
"merchantRefundId": "merchantRefundId",
"originalMerchantOrderId": "Refund-12345",
"amount": 50000,
"state": "COMPLETED",
"timestamp": 1730869961754,
"refundId": "OMR7878098045517540996",
"paymentDetails": [
{
"paymentMode": "UPI_INTENT",
"timestamp": 1706629419799,
"amount": 50000,
"transactionId": "OMR7896789",
"state": "COMPLETED",
"splitInstruments": [
{
"amount": 50000,
"rail": {
"type": "UPI",
"upiTransactionId": "upi12313",
"vpa": "abcd@ybl"
},
"instrument": {
"type": "ACCOUNT",
"accountType": "SAVINGS",
"accountNumber": "121212121212"
}
}
]
}
]
}
}Case 2: Original source of transaction = CARD
{
"event": "pg.refund.completed",
"payload": {
"merchantId": "merchantId",
"merchantRefundId": "merchantRefundId",
"originalMerchantOrderId": "Refund-12345",
"amount": 50000,
"state": "COMPLETED",
"timestamp": 1730869961754,
"refundId": "OMR7878098045517540996",
"paymentDetails": [
{
"paymentMode": "UPI_INTENT",
"timestamp": 1706629419799,
"amount": 50000,
"transactionId": "OMR7896789",
"state": "COMPLETED",
"splitInstruments": [
{
"amount": 50000,
"rail": {
"type": "PG",
"transactionId": "transactionId",
"authorizationCode": "authorizationCode",
"serviceTransactionId": "serviceTransactionId"
},
"instrument": {
"type": "CREDIT_CARD",
"bankTransactionId": "bankTransactionId",
"bankId": "bankId",
"arn": "arn",
"brn": "brn"
}
}
]
}
]
}
}Case 3: Original source of transaction = NET_BANKING
{
"originalMerchantOrderId": "TX1752742884088",
"amount": 50,
"state": "COMPLETED",
"refundId": "OMR2507211657281836129569",
"timestamp": 1753097248198,
"splitInstruments": [
{
"instrument": {
"type": "NET_BANKING",
"bankId": "SBIN",
"brn": "brn123"
},
"rail": {
"type": "PG"
},
"amount": 50
}
],
"paymentDetails": [
{
"transactionId": "OMR2507211657281836129569",
"paymentMode": "NET_BANKING",
"timestamp": 1753097248198,
"amount": 50,
"state": "COMPLETED",
"instrument": {
"type": "NET_BANKING",
"bankId": "SBIN"
},
"rail": {
"type": "PG"
},
"splitInstruments": [
{
"instrument": {
"type": "NET_BANKING",
"bankId": "SBIN"
},
"rail": {
"type": "PG"
},
"amount": 50
}
]
}
]
}Response for Refund Failed
Case 1: Original source of transaction = UPI
{
"event": "pg.refund.failed",
"payload": {
"originalMerchantOrderId": "",
"refundId": "OMRxxxxx",
"amount": 1234,
"state": "FAILED",
"timestamp": 1730869961754,
"refundId": "OMR7878098045517540996",
"errorCode": "AUTHORIZATION_ERROR",
"detailedErrorCode": "ZM",
"paymentDetails": [
{
"paymentMode": "UPI_INTENT",
"timestamp": 1706629419799,
"amount": 50000,
"transactionId": "OMR7896789",
"state": "COMPLETED",
"splitInstruments": [
{
"amount": 50000,
"rail": {
"type": "UPI",
"upiTransactionId": "upi12313",
"vpa": "abcd@ybl"
},
"instrument": {
"type": "ACCOUNT",
"accountType": "SAVINGS",
"accountNumber": "121212121212"
}
}
]
}
]
}
}Case 2: Original source of transaction = CARD
{
"event": "pg.refund.failed",
"payload": {
"originalMerchantOrderId": "",
"refundId": "OMRxxxxx",
"amount": 1234,
"state": "FAILED",
"timestamp": 1730869961754,
"refundId": "OMR7878098045517540996",
"errorCode": "AUTHORIZATION_ERROR",
"detailedErrorCode": "ZM",
"paymentDetails": [
{
"paymentMode": "UPI_INTENT",
"timestamp": 1706629419799,
"amount": 50000,
"transactionId": "OMR7896789",
"state": "COMPLETED",
"splitInstruments": [
{
"amount": 50000,
"rail": {
"type": "PG",
"transactionId": "transactionId",
"authorizationCode": "authorizationCode",
"serviceTransactionId": "serviceTransactionId"
},
"instrument": {
"type": "CREDIT_CARD",
"bankTransactionId": "bankTransactionId",
"bankId": "bankId",
"arn": "arn",
"brn": "brn"
}
}
]
}
]
}
}Case 3: Original source of transaction = NET_BANKING
{
"event": "pg.refund.failed",
"payload": {
"originalMerchantOrderId": "",
"refundId": "OMRxxxxx",
"amount": 1234,
"state": "FAILED",
"timestamp": 1730869961754,
"refundId": "OMR7878098045517540996",
"errorCode": "AUTHORIZATION_ERROR",
"detailedErrorCode": "ZM",
"paymentDetails": [
{
"paymentMode": "UPI_INTENT",
"timestamp": 1706629419799,
"amount": 50000,
"transactionId": "OMR7896789",
"state": "COMPLETED",
"splitInstruments": [
{
"amount": 50000,
"rail": {
"type": "PG",
"transactionId": "transactionId",
"authorizationCode": "authorizationCode",
"serviceTransactionId": "serviceTransactionId"
},
"instrument": {
"type": "NET_BANKING",
"bankTransactionId": "bankTransactionId",
"bankId": "bankId",
"arn": "arn",
"brn": "brn"
}
}
]
}
]
}
}What if Webhook fails?
If you don’t receive the Webhook callback, you can use the Order Status API to manually check the payment status.