Pagos únicos en efectivo

Antes de comenzar

Al finalizar este tutorial podrás recibir pagos en efectivo desde tu tienda en línea y recibir las notificaciones de los pagos en tiempo real.

📘

¿Qué necesito para comenzar?

Recomendamos tener [conocimientos de APIs] (https://asiermarques.com/2013/conceptos-sobre-apis-rest/).
Es preferible ser desarrollador fullstack (frontend + backend).
Debes instalar o incluir una de las librerías disponibles de Conekta.
Tener tus API Keys (llave personal) de pruebas.

Paso 1 - Agrega tu llave privada y versión del API

Copia tus llaves privadas de pruebas. Si aún no las tienes, puedes obtenerlas en el siguiente enlace:
Quiero obtener mis API keys .

require "conekta"
Conekta.api_key = "key_eYvWV7gSDkNYXsmr"
Conekta.api_version = "2.0.0"
# N/A
require_once("/path/to/lib/Conekta.php");
\Conekta\Conekta::setApiKey("key_eYvWV7gSDkNYXsmr");
\Conekta\Conekta::setApiVersion("2.0.0");
import conekta
conekta.api_key = "key_eYvWV7gSDkNYXsmr"
conekta.api_version = "2.0.0"
var conekta = require('conekta');
conekta.api_key = 'key_eYvWV7gSDkNYXsmr';
conekta.api_version = '2.0.0';
import com.conekta;
Conekta.setApiKey("key_eYvWV7gSDkNYXsmr");
com.conekta.Conekta.apiVersion = "2.0.0";
using conekta;
conekta.Api.apiKey = "key_eYvWV7gSDkNYXsmr";
conekta.Api.version = "2.0.0";
import (
    conekta "github.com/conekta/conekta-go"
)
conekta.APIKey = "key_pMcnDF4zFyWKyLG15LuqwA"

Paso 2 - Crea una orden

Al crear una orden se generará el cargo de tu cliente. Si no deseas crear el cargo de inmediato, puedes preparar la orden y después generar el cargo mandando la información del arreglo charges.

Los campos mostrados en el siguiente ejemplo son los mínimos que se requieren para continuar, si deseas saber más sobre el objeto llamado order, puedes revisar nuestra REST API para más información.

Si deseas que tu ficha de compra tenga un periodo de expiración, puedes agregar expires_at dentro de payment_method, es un campo tipo unix timestamp que te permite generar una ficha que expire ¡incluso segundos!

begin
  thirty_days_from_now = Time.now.advance(days: 30).to_i 

  order = Conekta::Order.create({
    line_items: [{
        name: "Tacos",
        unit_price: 1000,
        quantity: 12
    }],
    shipping_lines: [{
        amount: 1500,
        carrier: "FEDEX"
    }], #shipping_lines - physical goods only
    currency: "MXN",
    customer_info: {
        name: "Fulanito Pérez",
        email: "[email protected]",
        phone: "+5218181818181"
    },
    shipping_contact: {
        address: {
            street1: "Calle 123, int 2",
            postal_code: "06100",
            country: "MX"
        }
    }, #shipping_contact - required only for physical goods
    charges: [{
        payment_method: {
            type: "cash",
            expires_at: thirty_days_from_now
        }
    }]
  })
rescue Conekta::Error => error
  for error_detail in error.details do
    puts error_detail.message
  end
end
curl --request POST \
  --url https://api.conekta.io/orders \
  --header 'accept: application/vnd.conekta-v2.0.0+json' \
  -u key_eYvWV7gSDkNYXsmr: \
  --header 'content-type: application/json' \
  --data '{
    "line_items": [{
      "name": "Tacos",
      "unit_price": 1000,
      "quantity": 12
    }],
    "shipping_lines": [{
      "amount": 1500,
      "carrier": "FEDEX"
    }],
    "currency": "MXN",
    "customer_info": {
      "name": "Fulanito Pérez",
      "email": "[email protected]",
      "phone": "+5218181818181"
    },
    "shipping_contact":{
    "address": {
      "street1": "Calle 123, int 2",
      "postal_code": "06100",
      "country": "MX"
     }
    },
    "charges":[{
      "payment_method": {
        "type": "oxxo_cash",
        "expires_at": 1600871926
      }
    }]
  }'
try{
  $thirty_days_from_now = (new DateTime())->add(new DateInterval('P30D'))->getTimestamp(); 

  $order = \Conekta\Order::create(
    [
      "line_items" => [
        [
          "name" => "Tacos",
          "unit_price" => 1000,
          "quantity" => 12
        ]
      ],
      "shipping_lines" => [
        [
          "amount" => 1500,
          "carrier" => "FEDEX"
        ]
      ], //shipping_lines - physical goods only
      "currency" => "MXN",
      "customer_info" => [
        "name" => "Fulanito Pérez",
        "email" => "[email protected]",
        "phone" => "+5218181818181"
      ],
      "shipping_contact" => [
        "address" => [
          "street1" => "Calle 123, int 2",
          "postal_code" => "06100",
          "country" => "MX"
        ]
      ], //shipping_contact - required only for physical goods
      "charges" => [
        [
          "payment_method" => [
            "type" => "oxxo_cash",
            "expires_at" => $thirty_days_from_now
          ]
        ]
      ]
    ]
  );
} catch (\Conekta\ParameterValidationError $error){
  echo $error->getMessage();
} catch (\Conekta\Handler $error){
  echo $error->getMessage();
}
try:
  from datetime import datetime
  from datetime import timedelta 

  thirty_days_from_now = int((datetime.now() + timedelta(days=1)).timestamp()) 

  order = conekta.Order.create({
    "line_items": [{
        "name": "Tacos",
        "unit_price": 1000,
        "quantity": 12
    }],
    "shipping_lines": [{
        "amount": 1500,
        "carrier": "FEDEX"
    }], #shipping_lines - physical goods only
    "currency": "MXN",
    "customer_info": {
      "name": "Fulanito Pérez",
      "email": "[email protected]",
      "phone": "+5218181818181"
    },
    "shipping_contact":{
       "address": {
         "street1": "Calle 123, int 2",
         "postal_code": "06100",
         "country": "MX"
       }
    }, #shipping_contact - required only for physical goods
    "charges":[{
      "payment_method": {
        "type": "oxxo_cash",
        "expires_at": thirty_days_from_now
      }
    }]
  })
except conekta.ConektaError as e:
  print e.message
thirty_days_from_now = (Math.round(Date.now()/1000 + 60 \* 60 \* 24 \* 30)).toString() 

order = conekta.Order.create({
  "line_items": [{
      "name": "Tacos",
      "unit_price": 1000,
      "quantity": 12
  }],
  "shipping_lines": [{
      "amount": 1500,
      "carrier": "FEDEX"
  }], //shipping_lines - phyiscal goods only
  "currency": "MXN",
  "customer_info": {
    "name": "Fulanito Pérez",
    "email": "[email protected]",
    "phone": "+5218181818181"
  },
  "shipping_contact":{
     "address": {
       "street1": "Calle 123, int 2",
       "postal_code": "06100",
       "country": "MX"
     }
  }, //shipping_contact - required only for physical goods
  "charges":[{
    "payment_method": {
      "type": "oxxo_cash",
      "expires_at": thirty_days_from_now
    }
  }]
}, function(err, res) {
    console.log(res.toObject());
});
Long nowUnixTimestamp = System.currentTimeMillis();
Long thirtyDaysFromNowUnixTimestamp =  (nowUnixTimestamp + 30L \* 24 \* 60 \* 60 \* 1000) / 1000L;
String thirtyDaysFromNow = thirtyDaysFromNowUnixTimestamp.toString(); 

try{
  Order order = Order.create(
    new JSONObject("{"
      + "'line_items': [{"
          + "'name': 'Tacos',"
          + "'unit_price': 1000,"
          + "'quantity': 12"
      + "}],"
      + "'shipping_lines': [{"
          + "'amount': 1500,"
          + "'carrier': 'FEDEX',"
      + "}]," //shipping_lines - physical goods only
      + "'currency': 'MXN',"
      + "'customer_info': {"
        + "'name': 'Fulanito Pérez',"
        + "'email': '[email protected]',"
        + "'phone': '+5218181818181'"
      + "},"
      + "'shipping_contact':{"
         + "'address': {"
           + "'street1': 'Calle 123, int 2',"
           + "'postal_code': '06100',"
           + "'country': 'MX'"
         + "}"
       + "}," //shipping_contact - required only for physical goods
      + "'charges':[{"
        + "'payment_method': {"
          + "'type': 'oxxo_cash',"
          + "'expires_at': " + thirtyDaysFromNow
        + "}"
      + "}]"
    + "}"
    )
  );
}catch (Conekta::Error e) {
   System.out.println(e.details.get(0).message);
}
//Calculate an expiration time
DateTime thirtyDaysFromNowDateTime = DateTime.Now.AddDays(30);
long thirtyDaysFromNowUnixTimestamp = (Int64)(thirtyDaysFromNowDateTime.Subtract(new DateTime(1970, 1, 1)).TotalSeconds);
String thirtyDaysFromNow = thirtyDaysFromNowUnixTimestamp.ToString(); 

try{
  conekta.Order order = new conekta.Order ().create(@"{
    ""line_items"": [{
        ""name"": ""Tacos"",
        ""unit_price"": 1000,
        ""quantity"": 12
    }],
    ""shipping_lines"": [{
        ""amount"": 1500,
        ""carrier"": ""FEDEX""
    }]," + //shipping_lines - physical goods only
    @"""currency"": ""MXN"",
    ""customer_info"": {
      ""name"": ""Fulanito Pérez"",
      ""email"": ""[email protected]"",
      ""phone"": ""+5218181818181""
    },
    ""shipping_contact"":{
       ""address"": {
         ""street1"": ""Calle 123, int 2"",
         ""postal_code"": ""06100"",
         ""country"": ""MX""
       }
    }," + //shipping_contact - required only for physical goods only
    @"""charges"":[{
      ""payment_method"": {
        ""type"": ""oxxo_cash"",
        ""expires_at"": " + thirtyDaysFromNow + @"
      }
    }]
  }");
} catch (ConektaException e) {
  foreach (JObject obj in e.details) {
    System.Console.WriteLine("\n [ERROR]:\n");
    System.Console.WriteLine("message:\t" + obj.GetValue("message"));
    System.Console.WriteLine("debug:\t" + obj.GetValue("debug_message"));
    System.Console.WriteLine("code:\t" + obj.GetValue("code"));
  }
}
Address := &conekta.Address{
    Street1: "Street1",
    Street2: "Street2",
    City: "City",
    State: "State",
    Country: "Country",
    PostalCode: "PostalCode",
}

customerParams := &conekta.CustomerParams{
    Name: "Fulanito Perez",
    Phone: "5512345678",
    Email: "[email protected]",
}

lineItemParams := &conekta.LineItemsParams{
    Name: "Naranjas Robadas",
    UnitPrice: 10000,
    Quantity: 2,
}

shippingParams := &conekta.ShippingLinesParams{
    Amount: 200,
    TrackingNumber: "123",
    Carrier: "Test Carrier",
    Method: "method",
}

shippingContactParams := &conekta.ShippingContactParams{
    Phone: "55-5555-5555",
    Receiver: "Miguel",
    BetweenStreets: "BetweenStreets",
    Address: Address,
}

chargeParams := &conekta.ChargeParams{
    PaymentMethod: &conekta.PaymentMethodParams{
        Type: "oxxo_cash",
        ExpiresAt: time.Now().AddDate(0, 0, 90).Unix(),
    },
}

orderParams := &conekta.OrderParams{}
orderParams.Currency = "MXN"
orderParams.CustomerInfo = customerParams
orderParams.LineItems = append(orderParams.LineItems, lineItemParams)
orderParams.ShippingLines = append(orderParams.ShippingLines, shippingParams)
orderParams.ShippingContact = shippingContactParams
orderParams.Charges = append(orderParams.Charges, chargeParams)

ord, err := order.Create(orderParams)
if err != nil {
    code := err.(conekta.Error).Details[0].Code
    fmt.Printf(code)
} else {
    fmt.Println(ord)
}

Paso 3 - Procesa una respuesta

Una vez que creaste la orden y el cargo, debes procesar la respuesta y compartir la ficha de pago.
Recuerda que la respuesta de una orden es formato “JSON”

puts "ID: #{order.id}"
puts "Payment Method puede ser Cash u Oxxo"
puts "Payment Method: #{order.charges.first.payment_method.service_name}"
puts "Reference: #{order.charges.first.payment_method.reference}"
puts "$ #{(order.amount/100).to_f} #{order.currency}"
puts "Order"
puts "#{order.line_items.first.quantity}
      - #{order.line_items.first.name}
      - $ #{(order.line_items.unit_price/100).to_f}"


# Response
# ID: ord_2fsQdMUmsFNP2WjqS
# Payment Method puede ser Cash o Oxxo
# Payment Method: Cash
# Reference: 123456789012
# $ 135.0 MXN
# Order
# 12 - Tacos - $10.0
# N/A
echo "ID: ". $order->id;
echo "Payment Method:". $order->charges[0]->payment_method->service_name;
echo "Reference: ". $order->charges[0]->payment_method->reference;
echo "$". $order->amount/100 . $order->currency;
echo "Order";
echo $order->line_items[0]->quantity .
      "-". $order->line_items[0]->name .
      "- $". $order->line_items[0]->unit_price/100;


// Response
// ID: ord_2fsQdMUmsFNP2WjqS
// Payment Method: Cash
// Reference: 123456789012
// $ 135.0 MXN
// Order
// 12 - Tacos - $10.0
print("ID: " + order.id)
print("Payment Method: " + order.charges[0].payment_method.service_name)
print("Reference: " + order.charges[0].payment_method.reference)
print("$" + str(order.amount/100) + order.currency)
print("Order")
print(order.line_items[0].quantity + " - "
            + order.line_items[0].name + " - "
            + (order.line_items.unit_price/100))


# Response
# ID: ord_2fsQdMUmsFNP2WjqS
# Payment Method: Cash
# Reference: 123456789012
# $ 135.0 MXN
# Order
# 12 - Tacos - $10.0
console.log("ID: " + order.id);
console.log("Payment Method: " + order.charges[0].payment_method.service_name);
console.log("Reference: " + order.charges[0].payment_method.reference);
console.log("$" + (order.amount/100) + order.currency);
console.log("Order");
console.log(order.line_items[0].quantity + " - "
            + order.line_items[0].name + " - "
            + (order.line_items.unit_price/100));


// Response
// ID: ord_2fsQdMUmsFNP2WjqS
// Payment Method: Cash
// Reference: 123456789012
// $ 135.0 MXN
// Order
// 12 - Tacos - $10.0
OxxoPayment oxxoPayment = (OxxoPayment) charge.payment_method;


System.out.println("ID: " + order.id);
System.out.println("Payment Method: " + oxxoPayment.service_name);
System.out.println("Reference: " + oxxoPayment.reference);
System.out.println("$" + (order.amount/100) + order.currency);
System.out.println("Order");
System.out.println(order.line_items.get(0).quantity + " - "
            + order.line_items.get(0).name + " - "
            + (order.line_items.get(0).unit_price/100));


// Response
// ID: ord_2fsQdMUmsFNP2WjqS
// Payment Method: Cash
// Reference: 123456789012
// $ 135.0 MXN
// Order
// 12 - Tacos - $10.0
Console.WriteLine("ID: " + order.id);
Console.WriteLine("Payment Method: " + order.charges[0].payment_method.service_name);
Console.WriteLine("Reference: " + order.charges[0].payment_method.reference);
Console.WriteLine("$" + (order.amount/100) + order.currency);
Console.WriteLine("Order");
Console.WriteLine(order.line_items[0].quantity + " - "
            + order.line_items[0].name + " - "
            + (order.line_items.unit_price/100));


// Response
// ID: ord_2fsQdMUmsFNP2WjqS
// Payment Method: Cash
// Reference: 123456789012
// $ 135.0 MXN
// Order
// 12 - Tacos - $10.0
fmt.Println("ID: ", o.ID)
fmt.Println("Payment Method: ", o.Charges.Data[0].PaymentMethod.ServiceName)
fmt.Println("Reference: ", o.Charges.Data[0].PaymentMethod.Reference)
fmt.Println("$", o.Amount/100, o.Currency)
fmt.Println("------Order------")
fmt.Println(" - ", o.LineItems.Data[0].Quantity)
fmt.Println(" - ", o.LineItems.Data[0].Name)
fmt.Println(" - ", o.LineItems.Data[0].UnitPrice/100)

//Response
//ID:  ord_2nCsbdLXvdAgDVcKr
//Payment Method:  Cash
//Reference:  98000004875830
//$ 152 MXN
//------Order------
// -  1
// -  Producto 7
// -  152

Paso 4 - Presenta la ficha de pago

Utiliza la ficha de pago que muestre las cadenas que corresponden al pago en efectivo configurado para tu negocio. Esta ficha permitirá mostrarle a tus clientes la referencia de pago en efectivo junto con los pasos necesarios para completarlo en el punto de venta de su preferencia.

Referencia de pago "Oxxo"

300

Ficha de pago en efectivo con Conekta

Te recomendamos incluirla en tu Checkout de compra o enviarla por correo para ofrecer una mejor experiencia.
Puedes obtenerla en repo en Github.

En este link puedes descargar los logos de nuestra red de efectivo.

En este link podrás conocer los lineamientos de nuestra marca Conekta.

Paso 5 - Recibe notificaciones

Para poder confirmar los pagos que se realicen en efectivo deberás añadir un Webhook en tu Admin Conekta para recibir las notificaciones POST (HTTP JSON) correctamente.

Utiliza URLs públicos: Si no tienes un IP público o un domino a tu disposición, te recomendamos utilizar servicios como ultrahook o localtunnel. También puedes consultar la referencia sobre webhooks. Recomendamos escuchar eventos de order.paid así

{
	"object": {
		"livemode": false,
		"amount": 18500,
		"currency": "MXN",
		"payment_status": "paid",
		"amount_refunded": 0,
		"customer_info": {
			"name": "Fulanito Pérez",
			"email": "[email protected]",
			"phone": "+5218181818181",
			"object": "customer_info"
		},
		"shipping_contact": {
			"receiver": "JOSE  MORALES",
			"phone": "6691208528",
			"address": {
				"street1": "Calle 123, int 2",
				"postal_code": "06100",
				"country": "MX"
			},
			"id": "ship_cont_2s53K99C8GXVmCfPk",
			"object": "shipping_contact",
			"created_at": 0
		},
		"object": "order",
		"id": "ord_2s53K99C8GXVmCfPm",
		"created_at": 1656547670,
		"updated_at": 1656547702,
		"line_items": {
			"object": "list",
			"has_more": false,
			"total": 1,
			"data": [{
				"name": "Tacos",
				"unit_price": 1000,
				"quantity": 12,
				"object": "line_item",
				"id": "line_item_2s53K99C8GXVmCfPg",
				"parent_id": "ord_2s53K99C8GXVmCfPm",
				"metadata": {}
			}]
		},
		"shipping_lines": {
			"object": "list",
			"has_more": false,
			"total": 1,
			"data": [{
				"name": "Tacos",
				"amount": 1500,
				"carrier": "FEDEX",
				"object": "line_item",
				"id": "line_item_2s53K99C8GXVmCfPg",
				"parent_id": "ord_2s53K99C8GXVmCfPm",
				"metadata": {}
			}]
		},
		"tax_lines": {
			"object": "list",
			"has_more": false,
			"total": 0,
			"data": []
		},
		"charges": {
			"object": "list",
			"has_more": false,
			"total": 1,
			"data": [{
				"id": "62bce95741de276186407e41",
				"livemode": false,
				"created_at": 1656547671,
				"currency": "MXN",
				"payment_method": {
					"service_name": "OxxoPay",
					"barcode_url": "https://s3.amazonaws.com/cash_payment_barcodes/sandbox_reference.png",
					"object": "cash_payment",
					"type": "oxxo",
					"expires_at": 1656634070,
					"store_name": "OXXO",
					"reference": "98000013217610"
				},
				"object": "charge",
				"description": "Payment from order",
				"status": "paid",
				"amount": 13500,
				"paid_at": 1656547702,
				"fee": 537,
				"customer_id": "",
				"order_id": "ord_2s53K99C8GXVmCfPm"
			}]
		}
	},
	"previous_attributes": {}
}

Paso 6 - Notifica a tu cliente

Te recomendamos notificar a tu cliente vía mail el estatus de su compra.

Paso 7 - Prueba tu Webhook

Dentro de tu perfil de Administrador en Conekta, puedes probar la funcionalidad de tu Webhook

¿Qué necesito para probar mi Webhook? Solo agrega la URL de tu Webhook y los datos de acceso al Perfil de Administrador Conekta.

👍

¡Listo!

Recuerda cambiar tus llaves pública y privada de pruebas por tus llaves de producción después de realizar pruebas.