Crear cobros vía SPEI

En este tutorial aprenderás a generar y recibir pagos seguros por medio de transferencias bancarias a través de SPEI.

Tutorial para sobre

1 Entendiendo el flujo

Para recibir pagos a través de SPEI, es necesario crear un cargo en Conekta con los siguientes datos: importe, tipo de moneda y descripción. Como respuesta recibirás una CLABE como referencia única de pago que deberás enviar a tu cliente para efectúe su pago y pueda completar la compra.

Tus clientes podrán pagar por medio de transferencia electrónica o directamente en sucursales bancarias con efectivo o cheque.

Como el proceso de este método de pago es asíncrono y el estatus del cargo no cambiará a “Pagado” de manera automática, es necesario implementar “webhooks” para recibir las notifaciones que se generen.

Reproducir flujo animado


2 Realizando un Cargo

El primer paso es incluir una de nuestras librerías en tu proyecto. Una vez instalada la librería de tu preferencia necesitarás utilizar tu llave privada, de esta manera podrás autenticar todas las llamadas que realices.

Recuerda que solamente las llaves de producción pueden procesar pagos reales. Las llaves sandbox están diseñadas para realizar pruebas solamente. También, toma en cuenta que las llamadas del lado del servidor deberán utilizar las llaves privadas ya que, por cuestiones de seguridad, las llaves públicas tienen permisos limitados.

#N/A: curl -u {global_api_key}: \
require "conekta"
Conekta.api_key = "{global_api_key}"
require_once("/path/to/lib/Conekta.php");
\Conekta\Conekta::setApiKey("{global_api_key}");
import conekta
conekta.api_key = "{global_api_key}"
var conekta = require('conekta');
conekta.api_key = "{global_api_key}";
import com.conekta;
Conekta.setApiKey("{global_api_key}");
using conekta;
conekta.Api.apiKey = "{global_api_key}"; 

Ahora realizaremos una petición a Conekta con el importe, tipo de moneda y la descripción de la compra. También necesitamos especificar el tipo de cargo que estamos realizando (en este caso “spei”).

curl -H "Accept: application/vnd.conekta-v{global_api_version}+json" \
     -H "Content-type: application/json" \
     -u {global_api_key}: \
     -X POST -d '{
      "description":"Stogies",
      "amount": 20000,
      "currency":"MXN",
      "reference_id":"9839-wolf_pack",
      "bank": {
        "type": "spei"
      },
      "details": {
        "name": "Arnulfo Quimare",
        "phone": "403-342-0642",
        "email": "logan@x-men.org",
        "customer": {
          "logged_in": true,
          "successful_purchases": 14,
          "created_at": 1379784950,
          "updated_at": 1379784950,
          "offline_payments": 4,
          "score": 9
        },
        "line_items": [{
          "name": "Box of Cohiba S1s",
          "description": "Imported From Mex.",
          "unit_price": 20000,
          "quantity": 1,
          "sku": "cohb_s1",
          "category": "food"
        }],
        "billing_address": {
          "street1":"77 Mystery Lane",
          "street2": "Suite 124",
          "street3": null,
          "city": "Darlington",
          "state":"NJ",
          "zip": "10192",
          "country": "Mexico",
          "tax_id": "xmn671212drx",
          "company_name":"X-Men Inc.",
          "phone": "77-777-7777",
          "email": "purshasing@x-men.org"
        }
      }
    }' https://api.conekta.io/charges
charge = conekta.Charge.create({
  "description":"Stogies",
  "amount": 20000,
  "currency":"MXN",
  "reference_id":"9839-wolf_pack",
  "bank": {
    "type": "spei"
  },
  "details": {
    "name": "Arnulfo Quimare",
    "phone": "403-342-0642",
    "email": "logan@x-men.org",
    "customer": {
      "logged_in": true,
      "successful_purchases": 14,
      "created_at": 1379784950,
      "updated_at": 1379784950,
      "offline_payments": 4,
      "score": 9
    },
    "line_items": [{
      "name": "Box of Cohiba S1s",
      "description": "Imported From Mex.",
      "unit_price": 20000,
      "quantity": 1,
      "sku": "cohb_s1",
      "category": "food"
    }],
    "billing_address": {
      "street1":"77 Mystery Lane",
      "street2": "Suite 124",
      "street3": null,
      "city": "Darlington",
      "state":"NJ",
      "zip": "10192",
      "country": "Mexico",
      "tax_id": "xmn671212drx",
      "company_name":"X-Men Inc.",
      "phone": "77-777-7777",
      "email": "purshasing@x-men.org"
    }
  }
})
$charge = Conekta_Charge::create(array(
  'description'=> 'Stogies',
  'reference_id'=> '9839-wolf_pack',
  'amount'=> 20000,
  'currency'=>'MXN',
  'bank'=> array(
    'type'=> 'spei'
  ),
  'details'=> array(
    'name'=> 'Arnulfo Quimare',
    'phone'=> '403-342-0642',
    'email'=> 'logan@x-men.org',
    'customer'=> array(
      'logged_in'=> true,
      'successful_purchases'=> 14,
      'created_at'=> 1379784950,
      'updated_at'=> 1379784950,
      'offline_payments'=> 4,
      'score'=> 9
    ),
    'line_items'=> array(
      array(
        'name'=> 'Box of Cohiba S1s',
        'description'=> 'Imported From Mex.',
        'unit_price'=> 20000,
        'quantity'=> 1,
        'sku'=> 'cohb_s1',
        'category'=> 'food'
      )
    ),
    'billing_address'=> array(
      'street1'=>'77 Mystery Lane',
      'street2'=> 'Suite 124',
      'street3'=> null,
      'city'=> 'Darlington',
      'state'=>'NJ',
      'zip'=> '10192',
      'country'=> 'Mexico',
      'tax_id'=> 'xmn671212drx',
      'company_name'=>'X-Men Inc.',
      'phone'=> '77-777-7777',
      'email'=> 'purshasing@x-men.org'
    )
  )
));
conekta.Charge.create({
  "description":"Stogies",
  "amount": 20000,
  "currency":"MXN",
  "reference_id":"9839-wolf_pack",
  "bank": {
    "type": "spei"
  },
  "details": {
    "name": "Arnulfo Quimare",
    "phone": "403-342-0642",
    "email": "logan@x-men.org",
    "customer": {
      "logged_in": true,
      "successful_purchases": 14,
      "created_at": 1379784950,
      "updated_at": 1379784950,
      "offline_payments": 4,
      "score": 9
    },
    "line_items": [{
      "name": "Box of Cohiba S1s",
      "description": "Imported From Mex.",
      "unit_price": 20000,
      "quantity": 1,
      "sku": "cohb_s1",
      "category": "food"
    }],
    "billing_address": {
      "street1":"77 Mystery Lane",
      "street2": "Suite 124",
      "street3": null,
      "city": "Darlington",
      "state":"NJ",
      "zip": "10192",
      "country": "Mexico",
      "tax_id": "xmn671212drx",
      "company_name":"X-Men Inc.",
      "phone": "77-777-7777",
      "email": "purshasing@x-men.org"
    }
  }
}, function(err, res) {
    console.log(res.toObject());
});
JSONObject payment_params;

payment_params = new JSONObject("{"
  + "'description':'Stogies',"
  + "'amount': 20000,"
  + "'currency':'MXN',"
  + "'reference_id':'9839-wolf_pack',"
  + "'bank':{'type':'spei'},"
  + "'details': {"
    + "'name': 'Arnulfo Quimare',"
    + "'phone': '403-342-0642',"
    + "'email': 'logan@x-men.org',"
    + "'customer': {"
      + "'logged_in': true,"
      + "'successful_purchases': 14,"
      + "'created_at': 1379784950,"
      + "'updated_at': 1379784950,"
      + "'offline_payments': 4,"
      + "'score': 9"
    + "},"
    + "'line_items': [{"
      + "'name': 'Box of Cohiba S1s',"
      + "'description': 'Imported From Mex.',"
      + "'unit_price': 20000,"
      + "'quantity': 1,"
      + "'sku': 'cohb_s1',"
      + "'category': 'food'"
    + "}],"
    + "'billing_address': {"
      + "'street1':'77 Mystery Lane',"
      + "'street2': 'Suite 124',"
      + "'street3': null,"
      + "'city': 'Darlington',"
      + "'state':'NJ',"
      + "'zip': '10192',"
      + "'country': 'Mexico',"
      + "'tax_id': 'xmn671212drx',"
      + "'company_name':'X-Men Inc.',"
      + "'phone': '77-777-7777',"
      + "'email': 'purshasing@x-men.org'"
    + "}"
  + "}"
+ "}");

Charge conektaCharge = Charge.create(payment_params);
begin
  charge = Conekta::Charge.create({
    "description"=> "Stogies",
    "amount"=> 20000,
    "currency"=> "MXN",
    "reference_id"=> "9839-wolf_pack",
    "bank"=> {
      "type"=> "spei"
    },
    "details"=> {
      "name"=> "Arnulfo Quimare",
      "phone"=> "403-342-0642",
      "email"=> "logan@x-men.org",
      "customer"=> {
        "logged_in"=> true,
        "successful_purchases"=> 14,
        "created_at"=> 1379784950,
        "updated_at"=> 1379784950,
        "offline_payments"=> 4,
        "score"=> 9
      },
      "line_items"=> [{
        "name"=> "Box of Cohiba S1s",
        "description"=> "Imported From Mex.",
        "unit_price"=> 20000,
        "quantity"=> 1,
        "sku"=> "cohb_s1",
        "category"=> "food"
      }],
      "billing_address"=> {
        "street1"=>"77 Mystery Lane",
        "street2"=> "Suite 124",
        "city"=> "Darlington",
        "state"=>"NJ",
        "zip"=> "10192",
        "country"=> "Mexico",
        "tax_id"=> "xmn671212drx",
        "company_name"=>"X-Men Inc.",
        "phone"=> "77-777-7777",
        "email"=> "purshasing@x-men.org"
      }
    }
  })
rescue Conekta::ParameterValidationError => e
  puts e.message_to_purchaser 
#alguno de los parámetros fueron inválidos
rescue Conekta::ProcessingError => e
  puts e.message_to_purchaser 
#la tarjeta no pudo ser procesada
rescue Conekta::Error => e
  puts e.message_to_purchaser 
#un error ocurrió que no sucede en el flujo normal de cobros como por ejemplo un auth_key incorrecto
end
conekta.Charge charge = new conekta.Charge ().create(@"{
  ""description"":""Stogies"",
  ""amount"": 20000,
  ""currency"":""MXN"",
  ""reference_id"":""9839-wolf_pack"",
  ""bank"": {
    ""type"": ""spei""
  },
  ""details"": {
    ""name"": ""Arnulfo Quimare"",
    ""phone"": ""403-342-0642"",
    ""email"": ""logan@x-men.org"",
    ""customer"": {
      ""logged_in"": true,
      ""successful_purchases"": 14,
      ""created_at"": 1379784950,
      ""updated_at"": 1379784950,
      ""offline_payments"": 4,
      ""score"": 9
    },
    ""line_items"": [{
      ""name"": ""Box of Cohiba S1s"",
      ""description"": ""Imported From Mex."",
      ""unit_price"": 20000,
      ""quantity"": 1,
      ""sku"": ""cohb_s1"",
      ""category"": ""food""
    }],
    ""billing_address"": {
      ""street1"":""77 Mystery Lane"",
      ""street2"": ""Suite 124"",
      ""street3"": null,
      ""city"": ""Darlington"",
      ""state"":""NJ"",
      ""zip"": ""10192"",
      ""country"": ""Mexico"",
      ""tax_id"": ""xmn671212drx"",
      ""company_name"":""X-Men Inc."",
      ""phone"": ""77-777-7777"",
      ""email"": ""purshasing@x-men.org""
    }
  }
}");

Adicionalmente, y de manera opcional, podemos enviar una referencia para relacionar el cargo con tus propias órdenes, al igual que una fecha de expiración dentro del objeto bancario para establecer una fecha límite de pago.


3 Procesando una Respuesta

Una vez creado el cargo, debemos de procesar la respuesta y enviar la CLABE de pago a tu cliente. Esta referencia aparecerá en la respuesta, dentro del atributo payment_method.clabe.

# cURL no ofrece esta funcionalidad
puts charge.payment_method.clabe
puts charge.payment_method.bank
print($charge->payment_method->clabe);
print($charge->payment_method->bank);
print charge.payment_method.clabe
print charge.payment_method.bank
cconsole.log(charge.toObject().payment_method.clabe);
console.log(charge.toObject().payment_method.bank);
System.out.println(conektaCharge.payment_method.clabe);
System.out.println(conektaCharge.payment_method.bank);
Console.WriteLine(conektaCharge.payment_method.clabe);
Console.WriteLine(conektaCharge.payment_method.bank);

El atributo payment_method.bank siempre será “STP”.


4 Recibir la notificación

Al momento de procesar pagos de manera asíncrona, necesitarás recibir notificaciones para confirmar que los cargos realizados han sido pagados. Conekta enviará webhooks de todas las notificaciones de pagos asíncronas a tu servidor. Estas notificaciones son mensajes POST de tipo HTTP jSON. Cada notificación contiene un objeto jSON con información particular del evento y suscripción asociada.

Al configurar notificaciones de pago, necesitarás configurar los webhooks dentro de la sección de webhooks del admin. La configuración es simple. Todo lo que necesitas hacer es especificar el URL (o los URL) a donde te gustaría recibir notificaciones y Conekta enviará todas las notificaciones al (los) URL especificado(s).

Para operar las notificaciones en tu servidor, deberás configurar una ruta para el webhook que creaste, escribir código para analizar el mensaje POST y operar sobre él. Para más información sobre webhooks, ve a la sección de webhooks.

Ten en cuenta que el URL usado en la configuración de webhook debe poder ser accedido de manera pública. Si no tienes un IP público o un domino a tu disposición, sugerimos usar un servicio local->público como ultrahook o localtunnel. Abajo te compartimos un ejemplo de cómo usar ultrahook. Antes de utilizar ultrahook deberás registrarte en ultrahook para obtener una llave de API.

$ gem install ultrahook
$ echo "api_key: my-ultrahook-api-hey" > ~/.ultrahook
$ ultrahook webhook-test 5000

  Authenticated as conekta
  Forwarding activated...
  http://webhook-test.conekta.ultrahook.com -> http://localhost:5000

Para recibir notificaciones de webhooks, puedes usar el siguiente código:

# N/A
require 'json'

#example in sinatra
post '/my/webhook/url' do

  #Analizar la información del evento en forma de json
  event_json = JSON.parse(request.body.read)

  case event_json['type']
  when 'charge.paid'
    
  #Hacer algo con la información como actualizar los atributos de la orden en tu base de datos
      
  #charge = Charge.find(event_json['object']['id'])

  end
end
// Analizar la información del evento en forma de json
$body = @file_get_contents('php://input');
$event_json = json_decode($body);
http_response_code(200); // Return 200 OK

if ($event_json->type == 'charge.paid'){
 
 //Hacer algo con la información como actualizar los atributos de la orden en tu base de datos
 
 //charge = $this->Charge->find('first', array(
 
 //  'conditions' => array('Charge.id' => $event_json->object->id)
 
 //))
}
import json

# Analizar la información del evento en forma de json en Django
event_json = json.loads(HttpRequest.body)

if event_json.type == 'charge.paid':
  
#Hacer algo con la información como actualizar los atributos de la orden en tu base de datos
  
#charge = EcommerceCharge.objects.get(pk=event_json['object']['id'])
// Analizar la información del evento en forma de json en javascript
event_json = typeof req.body == 'string' ? JSON.parse(req.body) : req.body;

if (event_json.type == 'charge.paid') {
   //Hacer algo con la información como actualizar los atributos de la orden en tu base de datos
}
import org.json.JSONObject;
  protected void doPost(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
      BufferedReader get_body = request.getReader();
     
      //Parsear JSON y manejar respuesta
    }
// Analizar la información del evento en forma de json
var event = JObject.Parse('req.body');

if (event['type'] == 'charge.paid') {
   //Hacer algo con la información como actualizar los atributos de la orden en tu base de datos
}

Además de recibir notificaciones de charge.paid, también recibirás otra información útil como charge.created. Puedes ver una lista completa de eventos en la la referencia de eventos. Revisa la sección de webhooks para más información.


5 Ejemplos de implementación

Puedes descargar o consultar ejemplo de pago en SPEI en Github:
Ver ejemplo en Github Descargar tutorial


Revisa la página de webhooks para más información.