H Honor
ZH
DiDi finanzas

文档中心

提供接入指南、接口文档、测试指引等全流程接入所需信息

2.10 Refund Status Webhook

1. Solución Tecnológica de Webhook

BNPL api docs图 version 1_西语版-part2 - Webhook Tech Solution.jpg

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

Tipo de evento Nombre del evento Descripción del evento
Pago PAYMENT.ORDER.PAID El pago tiene éxito
PAYMENT.ORDER.CANCELED Pago cancelado
PAYMENT.ORDER.DENIED Pago denegado
PAYMENT.ORDER.EXPIRED Pago caducado.
El pedido caduca cuando no ha sido pagado.
El tiempo de expiración se refiere al campo expireTime de la solicitud, que es proporcionado por el socio.
Si el socio no proporciona este valor, el valor predeterminado es 24 horas.
Reembolso REFUND.ORDER.SUCCESS Reembolso exitoso
REFUND.ORDER.FAILED Reembolso fallido

3. Formato de Mensaje de Evento de Webhook

3.1 Webhook de Pago

Encabezado

Nombre del campo Descripción del campo Valor del parámetro Presente (Obligatorio/Opcional/Condicional) Observaciones
Content-Type Tipo de contenido application/json Obligatorio
X-Event-Timestamp Marca de tiempo por ejemplo, 1733619128 Obligatorio La información de la marca de tiempo.
Marca de tiempo en segundos. Se utiliza para el cálculo de la firma.
X-Event-Signature firma Obligatorio La información de la firma. El socio debe validar este valor.
X-Event-ID ID de evento único Obligatorio ID de evento único
X-Event-Topic Nombre del evento Obligatorio Nombre del evento. Puede consultar la "2. Lista de Eventos de Webhook".
X-Event-Delivery-ID ID de entrega (uuid) por ejemplo, "f81d4fae-7dec-11d0-a765-00a0c91e6bf6" Obligatorio ID de entrega único (UUID) para este intento de webhook.
Este campo no se calculará en la firma.



Cuerpo

Nombre del campo Tipo Descripción del campo Presente (Obligatorio/Opcional/Condicional) Observaciones
partnerOrderId String ID del documento de pago del lado del comerciante Obligatorio Enlace idempotente
Identificador del número de serie del pago del comerciante
partnerOrderAmount Long Monto del pedido Obligatorio El importe real del pago multiplicado por 100; este importe es el importe final después de aplicar el descuento del comerciante.
La unidad son los centavos. Ejemplo: para 99,99 pesos, el valor debe ser 9999.
Utilice la cantidad correspondiente a la moneda del peso mexicano.
currency String Moneda Obligatorio Norma ISO 4217 - México MXN
expireTime String Tiempo de expiración Opcional patrón: yyyy-MM-dd'T'HH:mm:ss.SSSXXX
por ejemplo, "2025-12-08T01:11:33.000+08:00". El desfase horario es obligatorio.
finishTime String Tiempo de finalización Opcional patrón: yyyy-MM-dd'T'HH:mm:ss.SSSXXX
por ejemplo, "2025-12-08T01:11:33.000+08:00". El desfase horario es obligatorio.
createTime String Hora de creación del pedido Opcional patrón: yyyy-MM-dd'T'HH:mm:ss.SSSXXX
por ejemplo, "2025-12-08T01:11:33.000+08:00". El desfase horario es obligatorio.
updateTime String Hora de la última actualización del estado del pedido Opcional patrón: yyyy-MM-dd'T'HH:mm:ss.SSSXXX
por ejemplo, "2025-12-08T01:11:33.000+08:00". El desfase horario es obligatorio.
rejectCode String Código de rechazo Opcional El valor existe en estado DENIED.
Solo existe cuando X-Event-Topic es PAYMENT.ORDER.DENIED; para otros tipos de eventos (como EXPIRED, CANCELED), este campo no existe o es una cadena vacía.
rejectMsg String Motivo de rechazo Opcional El valor existe en estado DENIED.
test Boolean Indicador de observación de orden de prueba. Opcional Prueba significa la transacción de prueba.



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

Nombre del campo Descripción del campo Valor del parámetro Presente (Obligatorio/Opcional/Condicional) Observaciones
Content-Type Tipo de contenido application/json Obligatorio
X-Event-Timestamp Marca de tiempo por ejemplo, 1733619128 Obligatorio La información de la marca de tiempo.
Marca de tiempo en segundos. Se utiliza para el cálculo de la firma.
X-Event-Signature firma Obligatorio La información de la firma. El socio debe validar este valor.
X-Event-ID ID de evento único Obligatorio ID de evento único
X-Event-Topic Nombre del evento Obligatorio Nombre del evento. Puede consultar la "2. Lista de Eventos de Webhook".
X-Event-Delivery-ID ID de entrega (uuid) por ejemplo, "f81d4fae-7dec-11d0-a765-00a0c91e6bf6" Obligatorio ID de entrega único (UUID) para este intento de webhook.
Este campo no se calculará en la firma.



Cuerpo

Nombre del campo Tipo Descripción del campo Presente (Obligatorio/Opcional/Condicional) Observaciones
partnerOrderId String ID del documento de pago del lado del comerciante Obligatorio Enlace idempotente
Identificador del número de serie del pago del comerciante
partnerRefundId String ID del formulario de reembolso del lado del comerciante Obligatorio Enlace idempotente
refundAmount Long Monto del reembolso Obligatorio Importe de reembolso real. La unidad del importe es centavos. Por ejemplo, si son 99,99 pesos, el valor aquí debe ser 9999.
Importe de Reembolso = suma (Precio Unitario de Reembolso × Cantidad de Reembolso)
currency String Moneda Obligatorio Norma ISO 4217 - México MXN
rejectCode String Código de rechazo Opcional El valor existe en estado DENIED
rejectMsg String Motivo de rechazo Opcional El valor existe en estado DENIED
refundTime String Tiempo de reembolso Obligatorio patrón: yyyy-MM-dd'T'HH:mm:ss.SSSXXX
por ejemplo, "2025-12-08T01:11:33.000+08:00". El desfase horario es obligatorio.
createTime String Hora de creación del pedido Opcional patrón: yyyy-MM-dd'T'HH:mm:ss.SSSXXX
por ejemplo, "2025-12-08T01:11:33.000+08:00". El desfase horario es obligatorio.
updateTime String Hora de la última actualización del estado del pedido Opcional patrón: yyyy-MM-dd'T'HH:mm:ss.SSSXXX
por ejemplo, "2025-12-08T01:11:33.000+08:00". El desfase horario es obligatorio.
test Boolean Indicador de observación de orden de prueba. Opcional Prueba significa la transacción de prueba.



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
reject_code
TradePayFail
SkuBlackList
RiskRuleRejected
RiskControlRejected
PayRiskReject
PaylaterRuleRejected
OverdueRepayment
LoggedOut
GeneralError
ExitClose
CreditReject
CreditNotEnough
CreditActiveReject
CategoryBlackList

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).

复制成功