1. Solución Tecnológica de Webhook

Cuando el socio crea una orden de pago o una orden de reembolso, el mensaje se enviará al sistema DiDi BNPL Core.
El sistema DiDi BNPL Webhook entregará el evento Webhook al sistema del socio.
2. Lista de Eventos de Webhook
3. Formato de Mensaje de Evento de Webhook
3.1 Webhook de Pago
Encabezado
Cuerpo
Ejemplo
{
"partnerOrderId": "202512XXXX031300011032631762517",
"partnerOrderAmount": 52231,
"currency": "MXN",
"expireTime": "2025-12-09T01:11:33.000+08:00",
"finishTime": "2025-12-08T01:12:08.000+08:00",
"createTime": "2025-12-08T01:11:33.000+08:00",
"updateTime": "2025-12-08T01:12:08.000+08:00",
"rejectCode": "",
"rejectMsg": "",
"test": true
}
3.2 Webhook de Reembolso
Encabezado
Cuerpo
Ejemplo
{
"partnerOrderId": "16000227_20260XXXX796022901278864",
"partnerRefundId": "16000227_KBOtXXXX20260127202943864",
"refundAmount": 9999,
"currency": "MXN",
"rejectCode": "",
"rejectMsg": "",
"refundTime": "2026-01-27T20:29:43.000+08:00",
"createTime": "2026-01-27T20:29:43.000+08:00",
"updateTime": "2026-01-27T20:29:48.000+08:00",
"test": true
}
4. Lista de Códigos de Rechazo para PAYMENT.ORDER.DENIED
El reject_code de esta lista se devuelve únicamente en los eventos PAYMENT.ORDER.DENIED, para indicar el motivo específico del fallo de pago. Los eventos como PAYMENT.ORDER.EXPIRED y PAYMENT.ORDER.CANCELED no devuelven este campo.
- rejectCode
5. URL de Webhook y Verificación de Firma
5.1 URL de Webhook
Proporcione las URL de Webhook de sus entornos de producción y de prueba al personal de DiDi para su configuración. A continuación, DiDi le enviará la clave de firma correspondiente al Webhook.
5.2 Verificación de Firma de Webhook
El Webhook devuelto contiene una firma en su encabezado. Debe desarrollar la función de verificación de firma para el mensaje de Webhook.
Puede consultar el código a continuación.
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;
/**
* HMAC-SHA512 Signature verification tools
* Format: eventId:timestamp:data
* Signature result: hexadecimal string
*/
public class HMACSHA512HexSignerVerify {
private static final String HMAC_SHA512 = "HmacSHA512";
private static final String DELIMITER = ":";
/**
* Verify signature
*
* @param eventId event ID, value description: X-Event-ID in the request header
* @param data data, value description: The complete response of the request
* @param signature The signature to be verified (hexadecimal string), value description: X-Event-Signature in the request header
* @param secretKey key, value description: The unique signature key generated when a tenant adds a subscription
* @param timestamp timestamp (in seconds), take X-Event-Timestamp from the header
* @param maxAgeSeconds signature maximum validity period (seconds), 0 indicates no validity period verification
* @return verification result
*/
public static boolean verifySignature(
String eventId,
String data,
String signature,
String secretKey,
long timestamp,
long maxAgeSeconds) {
try {
// Verify the validity period of the timestamp
if (maxAgeSeconds > 0) {
long currentTime = Instant.now().getEpochSecond();
if (Math.abs(currentTime - timestamp) > maxAgeSeconds) {
return false; // The signature has expired.
}
}
// Recalculate the signature
String expectedSignature = generateSignature(eventId, data, secretKey, timestamp);
// Security comparison of the signature
return constantTimeEquals(expectedSignature.toLowerCase(), signature.toLowerCase());
} catch (Exception e) {
return false;
}
}
/**
* Generate a signature (hexadecimal string with a timestamp)
*
* @param eventId event ID
* @param data data
* @param secretKey key
* @param timestamp Timestamp (in seconds)
* @return hexadecimal string signature
*/
public static String generateSignature(String eventId, String data, String secretKey, long timestamp) {
try {
// Build the payload
String payload = eventId + DELIMITER + timestamp + DELIMITER + data;
// Calculate HMAC-SHA512
Mac mac = Mac.getInstance(HMAC_SHA512);
SecretKeySpec secretKeySpec = new SecretKeySpec(
secretKey.getBytes(StandardCharsets.UTF_8),
HMAC_SHA512
);
mac.init(secretKeySpec);
byte[] hashBytes = mac.doFinal(payload.getBytes(StandardCharsets.UTF_8));
// Convert to a hexadecimal string
return bytesToHex(hashBytes);
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
throw new RuntimeException("Failed to generate the signature", e);
}
}
/**
* Convert a byte array to a hexadecimal string
*
* @param bytes byte array
* @return hexadecimal string
*/
private static String bytesToHex(byte[] bytes) {
StringBuilder hexString = new StringBuilder();
for (byte b : bytes) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
}
/**
* Secure string comparison (preventing timing attacks)
*
* @param str1 string1
* @param str2 string2
* @return whether they are equal
*/
private static boolean constantTimeEquals(String str1, String str2) {
if (str1 == null || str2 == null) {
return false;
}
if (str1.length() != str2.length()) {
return false;
}
int result = 0;
for (int i = 0; i < str1.length(); i++) {
result |= str1.charAt(i) ^ str2.charAt(i);
}
return result == 0;
}
}
6. Política de Entrega y Reintento de Webhook
Intentos máximos de entrega: Total de 10 intentos (incluida la entrega normal inicial, con 9 reintentos automáticos tras el fallo).
Condición de activación de reintento: El sistema del socio no devuelve un código HTTP dentro del rango [200, 300) al recibir el evento Webhook.
Estrategia de reintento: Estrategia de retroceso exponencial con base 2.
Regla de temporización de reintento: Suponga que el primer tiempo de reintento tras el fallo de entrega inicial es t; el tiempo de activación para cada reintento posterior de la cola es el siguiente: t, t+2, t+4, t+8, t+16, t+32, t+64, t+256, t+512, t+1024 (Unidad: segundo).