SPEI

Guía de Integración: Pago SPEI en OrkestaPay

Esta guía describe el flujo completo para integrar pagos mediante transferencia bancaria SPEI (Sistema de Pagos Electrónicos Interbancarios) en México usando la API de OrkestaPay.

Entorno de pruebas: Todos los ejemplos usan el endpoint de sandbox https://api.sand.orkestapay.com. Para producción, reemplaza el dominio con el endpoint correspondiente.


Resumen del flujo

1. Autenticación        →  Obtener access_token
2. Crear método de pago →  Obtener payment_method_id
3. Crear orden          →  Obtener order_id
4. Registrar pago       →  Obtener CLABE + referencia para el comprador

Recomendación: Guarda los IDs obtenidos en cada paso (payment_method_id, order_id, payment_id) ya que los necesitarás en pasos posteriores y para el seguimiento de la transacción.


Paso 1 — Autenticación

Obtén un access_token llamando al servicio de autenticación de OrkestaPay con tus credenciales de API. Este token se usa como Bearer en todos los demás endpoints.

📘 Documentación: https://docs.orkestapay.com/docs/autenticación

El token tiene un tiempo de expiración. Implementa la lógica de refresco o re-autenticación según lo indique la documentación.


Paso 2 — Crear método de pago SPEI

Registra una intención de pago de tipo SPEI. Esto genera un payment_method_id que usarás al momento de registrar el pago.

Request

curl --request POST \
     --url https://api.sand.orkestpay.com/v1/payment-methods \
     --header 'Accept: application/json' \
     --header 'Content-Type: application/json' \
     --header 'Authorization: Bearer {ACCESS_TOKEN}' \
     --data '{
         "type": "SPEI",
         "alias": "SPEI payment method"
     }'
CampoTipoDescripción
typestringDebe ser "SPEI" para transferencia bancaria en MX
aliasstringNombre descriptivo para identificar el método de pago

Response

{
    "payment_method_id": "pym_d8dc1499cd6f47ad9fb352ae35690c5c",
    "alias": "SPEI payment method",
    "type": "SPEI",
    "status": "ACTIVE",
    "created_at": "1781841077671",
    "updated_at": "1781841077671"
}

Guarda el payment_method_id — lo necesitarás en el Paso 4.

Nota sobre created_at / updated_at: Los timestamps están en formato Unix Epoch en milisegundos (ms). Para convertirlos a fecha legible: new Date(1781841077671) en JavaScript o datetime.fromtimestamp(1781841077671 / 1000) en Python.

📘 Documentación: https://docs.orkestapay.com/reference/create-payment-method


Paso 3 — Crear orden

Registra el detalle de la compra: artículos, montos y datos del cliente. Esto representa el "checkout" de la transacción.

Request

curl --request POST \
     --url https://api.sand.orkestapay.com/v1/orders \
     --header 'Accept: application/json' \
     --header 'Content-Type: application/json' \
     --header 'Authorization: Bearer {ACCESS_TOKEN}' \
     --data '{
         "country_code": "MX",
         "merchant_order_id": "1366656595193",
         "currency": "MXN",
         "subtotal_amount": 1000,
         "total_amount": 990,
         "discounts": [
             {
                 "amount": 10
             }
         ],
         "products": [
             {
                 "product_id": "7197",
                 "name": "Pantalla TCL Smart TV Serie A3 A343 HD Android TV 40",
                 "quantity": 1,
                 "unit_price": 1000
             }
         ],
         "customer": {
             "first_name": "John",
             "last_name": "Doe",
             "email": "[email protected]"
         }
     }'

Campos principales del body:

CampoTipoDescripción
country_codestringCódigo ISO del país. Para México: "MX"
merchant_order_idstringID único de tu sistema para esta orden (evita duplicados)
currencystringMoneda ISO 4217. Para pesos mexicanos: "MXN"
subtotal_amountnumberSuma de productos sin aplicar descuentos
total_amountnumberMonto final a cobrar (subtotal_amount - sum(discounts))
discountsarrayLista de descuentos aplicados. La suma debe corresponder a subtotal - total
productsarrayDetalle de artículos incluidos en la orden
customerobjectDatos del comprador

Response

{
    "order_id": "ord_466e5680ca3d44fa83045bd113d7a1df",
    "status": "CREATED",
    "expires_at": "1781927907513",
    "merchant_order_id": "1366656595193",
    "customer": {
        "source": "ORDER",
        "customer_id": "cus_21fa0cf02d1c4a73baf5b4f1c712c557",
        "first_name": "John",
        "last_name": "Doe",
        "email": "[email protected]",
        "created_at": "1781841507423",
        "updated_at": "1781841507423"
    },
    "placed_at": "1781841507513",
    "country": "México",
    "country_code": "MX",
    "currency": "MXN",
    "subtotal_amount": 1000,
    "discounts": [
        {
            "amount": 10.00
        }
    ],
    "total_amount": 990,
    "products": [
        {
            "product_id": "7197",
            "quantity": 1,
            "unit_price": 1000.00,
            "name": "Pantalla TCL Smart TV Serie A3 A343 HD Android TV 40"
        }
    ],
    "order_type": "STANDARD"
}

Guarda el order_id — lo necesitarás en el Paso 4.

Nota sobre expires_at: La orden tiene una vigencia limitada. Si el comprador no realiza la transferencia antes de esta fecha, la orden expirará y deberás crear una nueva. Muestra este plazo al usuario en tu interfaz.

📘 Documentación: https://docs.orkestapay.com/reference/create-order


Paso 4 — Registrar pago

Con el payment_method_id y el order_id obtenidos en los pasos anteriores, registra el pago. La respuesta incluirá la CLABE y referencia que el comprador debe usar para realizar la transferencia desde su banca en línea.

Request

curl --request POST \
     --url https://api.sand.orkestapay.com/v1/payments \
     --header 'Accept: application/json' \
     --header 'Content-Type: application/json' \
     --header 'Authorization: Bearer {ACCESS_TOKEN}' \
     --header 'Idempotency-Key: {UUID_UNICO_POR_INTENTO}' \
     --data '{
         "payment_source": {
             "type": "SPEI",
             "payment_method_id": "{PAYMENT_METHOD_ID}"
         },
         "order_id": "{ORDER_ID}"
     }'
HeaderDescripción
Idempotency-KeyUUID único por cada nuevo intento de pago. Reutilizar el mismo valor en un reintento evita pagos duplicados.

Importante sobre Idempotency-Key: Genera un nuevo UUID para cada intento de pago distinto (e.g., crypto.randomUUID() en Node.js o uuid.uuid4() en Python). Si haces un reintento exactamente del mismo pago ante un error de red, puedes reutilizar el mismo key para garantizar idempotencia.

Response

{
    "payment_id": "pay_bcd1d0cd28964567af02825088cd1bc2",
    "order_id": "ord_466e5680ca3d44fa83045bd113d7a1df",
    "status": "PAYMENT_ACTION_REQUIRED",
    "payment_source": {
        "type": "SPEI",
        "payment_method_id": "pym_d8dc1499cd6f47ad9fb352ae35690c5c"
    },
    "amount": {
        "requested": 102.00,
        "currency": "MXN"
    },
    "user_action_required": {
        "type": "OFFLINE_PAYMENT",
        "offline_payment_provider": {
            "reference": "1282938",
            "bank": "Finco Pay",
            "clabe": "734180000066931684",
            "url_payment_receipt": "https://api.sand.orkestapay.com/public/v1/offline-payments/bank-transfers/0e5264367c474b0481fe777bd2e32587/receipt"
        }
    },
    "transactions": [
        {
            "type": "REGISTER",
            "transaction_id": "079d36ffe02b4979bb1754b560f837a3",
            "status": "SUCCESS",
            "amount": 102.00,
            "provider": {
                "merchant_provider_id": "mpv_c102dd2cef374873b16153616ebaf90c",
                "provider_id": "prc_40000c08f7a649b1ab065b6158b2e0dc",
                "name": "bank_transfer"
            },
            "created_at": "1781841416576"
        }
    ],
    "created_at": "1781841409679",
    "updated_at": "1781841409679"
}

Campos clave de la respuesta:

CampoDescripción
statusSiempre será PAYMENT_ACTION_REQUIRED para SPEI — el pago está pendiente de la transferencia del comprador
user_action_required.offline_payment_provider.clabeCLABE interbancaria de 18 dígitos a la que el comprador debe transferir
user_action_required.offline_payment_provider.referenceReferencia numérica que el comprador debe incluir en la transferencia
user_action_required.offline_payment_provider.bankNombre del banco receptor
user_action_required.offline_payment_provider.url_payment_receiptURL para descargar el recibo con instrucciones de pago para el comprador

Flujo post-pago: El estado PAYMENT_ACTION_REQUIRED indica que la orden está activa y esperando la transferencia. OrkestaPay notificará el cambio de estado (e.g., COMPLETED o FAILED) vía webhook cuando el banco confirme la operación. Configura tu endpoint de webhook en el dashboard de OrkestaPay para recibir estas notificaciones.

📘 Documentación: https://docs.orkestapay.com/reference/create-payment


Resumen de IDs a persistir

IDObtenido enPara qué sirve
access_tokenPaso 1Autenticación en todos los endpoints
payment_method_idPaso 2Referenciar el método SPEI al crear el pago
order_idPaso 3Asociar la orden al pago; consultas de estado de orden
payment_idPaso 4Seguimiento y conciliación del pago
clabe + referencePaso 4Instrucciones que el comprador necesita para transferir

Errores comunes

EscenarioCausa probable
401 Unauthorizedaccess_token expirado o inválido — re-autentica
Pago duplicadoSe envió la misma Idempotency-Key para dos intentos distintos
Orden expirada al pagarEl comprador no transfirió antes de expires_at — crea una nueva orden