Con la integración de CoDi®, tu comercio adquirirá la capacidad de generar códigos de cobro en forma de códigos QR CoDi® que podrán ser desplegados en el punto de venta para ser escaneados por los usuarios desde el escáner de su cámara o aplicación bancaria para, posteriormente, realizar el pago desde su dispositivo.
En esta documentación encontrarás toda la información necesaria para:
En caso de tener alguna duda, estaremos felices de ayudarte a través del botón de contacto que se encuentra en cada una de las secciones de esta documentación.
dapp® cuenta con un sandbox para poder realizar pruebas durante el desarrollo.
Las URL base para cada ambiente son las siguientes:
Sandbox:
https://sandbox.dapp.mx/v2/
Producción:
https://api.dapp.mx/v2/
dapp® utiliza "Basic Access Authentication" para el REST API, usando el API KEY como password y dejando el usuario vacío
También debe incluirse el header “User-Agent” de lo contrario se rechazará la petición con un código 403, se recomienda incluir un nombre distintivo y un número de versión de aplicación, sin embargo cualquier valor será aceptado, ejemplo:
“User-Agent”: “MiIntegracion1.0”
curl --location --request GET 'https://sandbox.dapp.mx/v2' \ --header 'Authorization: Basic OnlvdXItYXBpLWtleQ=='
var request = new RestRequest(Method.GET); request.AddHeader("Authorization", "Basic OnlvdXItYXBpLWtleQ==");
Request request = new Request.Builder() .addHeader("Authorization", "Basic OnlvdXItYXBpLWtleQ==")
url = "https://sandbox.dapp.mx/v2" payload={} headers = { 'Authorization': 'Basic OnlvdXItYXBpLWtleQ==', } response = requests.request("GET", url, headers=headers, data=payload)
$curl = curl_init(); curl_setopt_array($curl, [ CURLOPT_URL => "https://sandbox.dapp.mx/v2", CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => "", CURLOPT_MAXREDIRS => 10, CURLOPT_TIMEOUT => 30, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLOPT_CUSTOMREQUEST => "GET", CURLOPT_HTTPHEADER => [ "Authorization: Basic OnlvdXItYXBpLWtleQ==" ], ]); $response = curl_exec($curl); curl_close($curl);
Para generar un Código QR de cobro, se debe consumir el siguiente endpoint:
/dapp-codes/
https://sandbox.dapp.mx/v2/dapp-codes/
POST
Privada
Parámetro |
Descripción |
Requerido u opcional |
---|---|---|
amount |
Monto, máximo 10 dígitos. Ejemplo: 10.0 |
Requerido |
description |
Descripción del cobro. Máximo 200 caracteres. Ejemplo: “chocolates”. |
Requerido |
qr_source |
Tipo de Código QR a generar. Valor para generar CoDi®: 1. |
Requerido |
reference |
Referencia interna del comercio. Máximo 200 caracteres. |
Opcional |
expiration_minutes |
Tiempo de expiración del código. Default 6 días. |
Opcional |
store |
Identificador de la sucursal que va a desplegar el código. |
Opcional |
pos |
Identificador de la caja que va a desplegar el código. |
Opcional |
curl --location --request POST 'https://sandbox.dapp.mx/v2/dapp-codes/' \ --header 'User-Agent: MyApp 1.0' \ --header 'Authorization: Basic OnlvdXItYXBpLWtleQ==' \ --header 'Content-Type: application/json' \ --data-raw '{ "amount":100.00, "description":"compra qr" }'
var client = new RestClient("https://sandbox.dapp.mx/v2/dapp-codes/"); client.Timeout = 60; var request = new RestRequest(Method.POST); client.UserAgent = "MyApp 1.0"; request.AddHeader("Authorization", "Basic OnlvdXItYXBpLWtleQ=="); request.AddHeader("Content-Type", "application/json"); var body = @"{" + "\n" + @" ""amount"":100.00," + "\n" + @" ""description"":""compra qr""" + "\n" + @"}"; request.AddParameter("application/json", body, ParameterType.RequestBody); IRestResponse response = client.Execute(request); Console.WriteLine(response.Content);
OkHttpClient client = new OkHttpClient().newBuilder() .build(); MediaType mediaType = MediaType.parse("application/json"); RequestBody body = RequestBody.create(mediaType, "{\n \"amount\":100.00,\n \"description\":\"compra qr\"\n}"); Request request = new Request.Builder() .url("https://sandbox.dapp.mx/v2/dapp-codes/") .method("POST", body) .addHeader("User-Agent", "MyApp 1.0") .addHeader("Authorization", "Basic OnlvdXItYXBpLWtleQ==") .addHeader("Content-Type", "application/json") .build(); Response response = client.newCall(request).execute();
import requests import json url = "https://sandbox.dapp.mx/v2/dapp-codes/" payload = json.dumps({ "amount": 100, "description": "compra qr" }) headers = { 'User-Agent': 'MyApp 1.0', 'Authorization': 'Basic OnlvdXItYXBpLWtleQ==', 'Content-Type': 'application/json' } response = requests.request("POST", url, headers=headers, data=payload)
$curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_URL => 'https://sandbox.dapp.mx/v2/dapp-codes/', CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => '', CURLOPT_MAXREDIRS => 10, CURLOPT_TIMEOUT => 0, CURLOPT_FOLLOWLOCATION => true, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLOPT_CUSTOMREQUEST => 'POST', CURLOPT_POSTFIELDS =>'{ "amount": 100.00, "description": "compra qr" }', CURLOPT_HTTPHEADER => array( 'User-Agent' => 'MyApp 1.0', 'Authorization: Basic OnlvdXItYXBpLWtleQ==', 'Content-Type: application/json', ), )); $response = curl_exec($curl); curl_close($curl);
{
"rc": 0,
"msg": "Ok",
"data": {
"id: "XM5BOqZ6",
"creation_date": "2018-03-28T00:21:51.576126-06:00",
"expiration_date": "2018-04-03T01:21:51.576126-05:00",
"description": "chocolates",
"amount": "5.00",
"currency": "MXN",
"qr_str": "https://dapp.mx/c/XM5BOqZ6",
"reference_num": "123456",
"qr_image": "https://media.dapp.mx/codigocobroqr/a6d6f4b5c4604015993f4cad21d8de16_1522218111.jpg",
}
}
Parámetro |
Descripción |
---|---|
id |
Id del Código QR. |
creation_date |
Fecha de creación del Código QR. |
expiration_date |
Fecha de vencimiento del Código QR. |
description |
Descripción del pago. |
amount |
Monto del pago. |
currency |
Moneda del pago. |
qr_str |
Cadena que conforma el Código QR |
reference_num |
Número de referencia. |
qr_image |
Contiene una URL de la imagen del Código QR. |
El comercio debe exponer un servicio que acepte peticiones donde dapp® le enviará una notificación cuando el usuario haya realizado el pago.
{
"id": "28e62e93-c26b-4c26-a25b-7aea2bbbfbad",
"amount": "5.00",
"currency": "MXN",
"reference": null,
"reference_num": "123456",
"description": "chocolates",
"date": "2018-03-28T06:24:49.167657+00:00",
"code": "XM5BOqZ6",
"refunded": 0,
"payments": [
{
"id": "821b9950-a60c-4bf5-b9ee-02b290e1e6c4",
"amount": "5.00",
"currency": "MXN",
"reference_num": "123456",
"Product": {
"id": 1,
"name": "saldo"
},
"wallet": "QR Pago",
"client": {
"name": "Nombre del cliente"
}
}
],
"security": {
"key": "1020a1fd5a0a127eadb044ce2e96f009",
"version": 1,
"signature": "ikekwTR5jKd9mXlijovOmTkVjm3dPNrgEas0IZ1ooqAXvuiZh/HEWezyQAnNT/4Pq0QSLE9930ty4+ESg74sNLVoiAbbHWKfxQjIViXCUG79wya9s/4ZW1cpN5ZdKXJ6HRs095SQrRAZzfQprMc/csZ3+d4qRwzwADfRfKCE52X311XJY5FnQcI2v/kMx8H3oogMWzbb0PP0MFYmZb7/x2BtNMlog3KrRCQdHL2PgfvrBRLbQvKAmACIbb6yPIiKtMgj43nwJoz+pypFUPW3zsh5tOxTiwOmpBrhB1HhhVv0lO23qw73N1VrY1o2IIXrCWWDK1x4KW265r9SAo2/Fg=="
}
}
Sección |
Parámetro |
Descripción |
|
---|---|---|---|
|
id |
Ticket id de la operación |
|
|
amount |
Indica el monto de la operación. |
|
|
currency |
Indica la moneda. |
|
|
reference |
Referencia interna del comercio. |
|
|
reference_num |
Número de referencia. |
|
|
description |
Descripción del cobro. |
|
|
date |
Fecha en que se realizó la operación. |
|
|
code |
Código identificador del código QR. |
|
|
refunded |
Estatus del reverso. ‘0’= no, ‘1’ = reverso total. |
|
Payments |
Id |
Id del pago. |
|
amount |
Monto del pago. |
||
currency |
Moneda del pago. |
||
reference_num |
Número de referencia. |
||
Product |
Id |
Id del producto |
|
name
|
Nombre del producto. Recibe 1 = saldo, 2 = CoDi®, 3 = Crédito, 4 = bnpl, 5 = vales, 6 = tarjeta de débito, 7 = tarjeta de crédito, 8 = cashin, 9 = cashout, 10 = crédito 3 msi, 11 = crédito 6 msi, 12 = crédito 9 msi, 13 = crédito 12 msi. 14 = crédito 18 msi, 15 = crédito 24 msi, 16 = Enrolamiento de tarjetas
|
||
wallet |
Nombre del wallet pagador. |
||
Client |
name |
Nombre del cliente. |
El JSON de la petición contiene una firma digital como método de autenticación. La firma consiste en una cadena formada por los campos id, currency, amount, description, reference, date. Si alguno de los datos es “null” se debe dejar vacío.
Ejemplo de cadena antes del hash28e62e93-c26b-4c26-a25b-7aea2bbbfbad|MXN|5.00|chocolates||2018-03-28T06:24:49.167657+00:00
Las llaves públicas que se deben utilizar para realizar esta validación se encuentran en el siguiente enlace:
https://media.dapp.mx/sw/dapp_public_keys.zip
static void Main(string[] args) { var cadena = "3c6f084f-3949-4236-a0bc-d85ff9e05495|MXN|10.0|Pago de prueba QR 5|Prueba 5|2021-06-07T17:04:56.900046+00:00"; var cadenaBytes = Encoding.UTF8.GetBytes(cadena); var firma = "hxkqkKaar5jstZMJeCK3Bv3IByGOZrVyb9t+kt1hEL7pt/FrvxATuc+9b/95lkqkeVSnK98/cD0xSs0p2fdkAhskHZJTHbTWu3bbNHWBpIXmqnRNzuhi7zP+tmwsaGJ830o1BL5Vyq5rvqq4ll/ILkJVCEbru4VwE/+0W7OWiETV4Nqe6M9Yhu09/+GtW0KCAgVaX8Yol3SgGGp8z6+T1WJ6hw6K5EIg34UfBujJzHfr+39LpBJvPO2JykGs1lwNrOaon+FYb8AzmVoRAu49VwJ380Gb3Wv6j1oy+O/8Ry3X0b8NY83JA9mdtz5/M0pIVvgqIcNjKH4KBuambSig1g=="; var firmaBytes = Convert.FromBase64String(firma); var fileStream = File.OpenText("../../../dapp_public_prod.pem"); var pemReader = new PemReader(fileStream); var keyParameter = (AsymmetricKeyParameter)pemReader.ReadObject(); ISigner signer = SignerUtilities.GetSigner("SHA-512withRSA"); signer.Init(false, keyParameter); signer.BlockUpdate(cadenaBytes, 0, cadenaBytes.Length); Console.WriteLine(signer.VerifySignature(firmaBytes)); }
import java.io.IOException; import java.math.BigInteger; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.security.InvalidKeyException; import java.security.KeyFactory; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.Signature; import java.security.SignatureException; import java.security.interfaces.RSAPublicKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; import java.util.List; import javax.crypto.BadPaddingException; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; public class dapp { public static String encryptThisString(String input) { try { // getInstance() method is called with algorithm SHA-512 MessageDigest md = MessageDigest.getInstance("SHA-512"); // digest() method is called // to calculate message digest of the input string // returned as array of byte byte[] messageDigest = md.digest(input.getBytes()); // Convert byte array into signum representation BigInteger no = new BigInteger(1, messageDigest); // Convert message digest into hex value String hashtext = no.toString(16); // Add preceding 0s to make it 32 bit while (hashtext.length() < 32) { hashtext = "0" + hashtext; } // return the HashText return hashtext; } // For specifying wrong message digest algorithms catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } } private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); public static String bytesToHex(byte[] bytes) { char[] hexChars = new char[bytes.length * 2]; for (int j = 0; j < bytes.length; j++) { int v = bytes[j] & 0xFF; hexChars[j * 2] = HEX_ARRAY[v >>> 4]; hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F]; } return new String(hexChars); } // Driver code public static void main(String args[]) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException, InvalidKeyException, NoSuchPaddingException, BadPaddingException, IllegalBlockSizeException, NoSuchProviderException, SignatureException { String cadena = "3c6f084f-3949-4236-a0bc-d85ff9e05495|MXN|10.0|Pago de prueba QR 5|Prueba 5|2021-06-07T17:04:56.900046+00:00"; String signature = "hxkqkKaar5jstZMJeCK3Bv3IByGOZrVyb9t+kt1hEL7pt/FrvxATuc+9b/95lkqkeVSnK98/cD0xSs0p2fdkAhskHZJTHbTWu3bbNHWBpIXmqnRNzuhi7zP+tmwsaGJ830o1BL5Vyq5rvqq4ll/ILkJVCEbru4VwE/+0W7OWiETV4Nqe6M9Yhu09/+GtW0KCAgVaX8Yol3SgGGp8z6+T1WJ6hw6K5EIg34UfBujJzHfr+39LpBJvPO2JykGs1lwNrOaon+FYb8AzmVoRAu49VwJ380Gb3Wv6j1oy+O/8Ry3X0b8NY83JA9mdtz5/M0pIVvgqIcNjKH4KBuambSig1g=="; String publicKeyContent8 = null; String file8 = "resource/dapp_public_pk8_prod.pem"; Path path8 = Paths.get(file8); List<String> lines8 = Files.readAllLines(path8); publicKeyContent8 = lines8.toString().replaceAll("\\n", "").replace("[-----BEGIN PUBLIC KEY-----", "").replace("-----END PUBLIC KEY-----]", "").replaceAll(", ", ""); KeyFactory kf8 = KeyFactory.getInstance("RSA"); X509EncodedKeySpec keySpecX5098 = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyContent8)); RSAPublicKey pubKey8 = (RSAPublicKey) kf8.generatePublic(keySpecX5098); final Signature sig = Signature.getInstance( "SHA512withRSA"); sig.initVerify( pubKey8 ); sig.update( cadena.getBytes("utf8") ); final byte[] signatureBytes = Base64.getDecoder().decode(signature); System.out.println("\n*Signature decode Base64*: " + bytesToHex(signatureBytes)); System.out.println("\n*Verificar*: " + sig.verify( signatureBytes )); } }
import base64 import os from Cryptodome.Hash import SHA512 from Cryptodome.PublicKey import RSA from Cryptodome.Signature import pkcs1_15 def test_signature(): cadena = "3c6f084f-3949-4236-a0bc-d85ff9e05495|MXN|10.0|Pago de prueba QR 5|Prueba 5|2021-06-07T17:04:56.900046+00:00" hash_cadena = SHA512.new(cadena.encode("utf-8")) signature = "hxkqkKaar5jstZMJeCK3Bv3IByGOZrVyb9t+kt1hEL7pt/FrvxATuc+9b/95lkqkeVSnK98/cD0xSs0p2fdkAhskHZJTHbTWu3bbNHWBpIXmqnRNzuhi7zP+tmwsaGJ830o1BL5Vyq5rvqq4ll/ILkJVCEbru4VwE/+0W7OWiETV4Nqe6M9Yhu09/+GtW0KCAgVaX8Yol3SgGGp8z6+T1WJ6hw6K5EIg34UfBujJzHfr+39LpBJvPO2JykGs1lwNrOaon+FYb8AzmVoRAu49VwJ380Gb3Wv6j1oy+O/8Ry3X0b8NY83JA9mdtz5/M0pIVvgqIcNjKH4KBuambSig1g==" b64_bytes = signature.encode("utf-8") byte_str = base64.b64decode(b64_bytes) path = os.path.join("MyPath", 'dapp_public_prod.pem') with open(path, 'r') as myfile: key_data = myfile.read() key = RSA.importKey(key_data) pkcs1_15.new(key).verify(hash_cadena, byte_str)
const CERT_FILE = './dapp_public_sb.pem'; $data = [ "id" => "fe6f8e08-8e38-49b1-8a93-2f9f1dd22ab6", "currency" => "MXN", "amount" => 10, "tip" => 0, "code" => "ejWOEX1u", "reference" => null, "reference_num" => "130953", "description" => "Pago Test Wallet Negocio: pago", "date" => "2024-04-09T01:27:54.058487+00:00", "redirect_url" => null, "refunded" => false, "client" => [ "id" => "", "name" => "Javier Torres" ], "payment" => [ "id" => "797583ee-fc3e-4772-83a1-7941efabea71", "amount" => 10, "currency" => "MXN", "type" => 0, "type_description" => "Balance", "wallet" => "Test Wallet Negocio", "wallet_id" => "328", "reference_num" => "130953", "reference" => "123456789012", "product" => [ "id" => 1, "name" => "saldo" ], "auth_num" => "130953", "refunds" => [] ], "security" => [ "key" => "3d4612aef1abdeebc9947a04d16da838", "version" => 1, "signature" => "P8zskMjj3MNHrjZf/NPw+Qju0Eh3V3ykkpK/OoA7XQQXijnM2zK2hrhrHezXqCtlPSfaN2OqCnVK05tCnLuyxBRUg31EztMlUIFxfUd2JoX6fqdwq+46PIv5apPVXHxuUlGHat+2OxvmXbos59sdaEDKJvQ/EeHALb7EAfH8AdgHO2hMrnFSH5BMw0j+E+S9UIF8Z2D9T6/SpYvnDDmDnvj1QbBqMel2PudnJnrje3WDTjB86zVrOYIZyZekBTGA4zwzfwVF2Ev9cGmBVjSnOSw5YH33O81jZ7YAI6sBI3NuTUrRuqew8nRKqVZ7lm0BmzfwoPP1Na7RiZWrH5s2+Q==" ] ]; $amount = preg_replace('/^(\d+\.\d)0$/', '\1', number_format($data['amount'], 2)); $original_string = $data['id'] . '|' . $data['currency'] . '|' . $amount . '|' . $data['description'] . '|' . $data['reference'] . '|' . $data['date']; if (isset($data['security'])) { $fp = fopen(CERT_FILE, "r"); $public_key_file = fread($fp, 8192); fclose($fp); $pkey_public = openssl_pkey_get_public($public_key_file); $signature_decoded = base64_decode($data['security']['signature']); $ok = openssl_verify($original_string, $signature_decoded, $pkey_public, OPENSSL_ALGO_SHA512); if ($ok == 0) { echo 'Firma invalida'; } else { echo 'Firma ok'; } }
Te dejamos aquí algunos términos relevantes definidos para facilitar la lectura y entendimiento de nuestras documentaciones.
Código de cobro:
Es el identificador único asignado a una transacción de cobro generada
por un comercio y que está asociado a ciertos parámetros específicos como Nombre del Comercio, Monto,
Moneda, Fecha, Lugar y Hora, entre otros. Un código de cobro puede ser representado como un Código QR para
ser escaneado por el cliente, o ser transferido a un dispositivo móvil a través de un deep link o una
notificación push.
Código QR:
Un código de respuesta rápida (o Código QR) es una representación gráfica
bidimensional de una cadena de texto generado por que generalmente se asocia a un código de cobro.
Código QR dinámico:
Es aquél Código QR asociado a un solo código de cobro y a una sola
transacción única y diferenciable. Sus parámetros pueden tomar valores distintos en cada transacción. Cada
Código QR se verá distinto a los demás ya que representará una cadena de texto distinta.
Código QR estático:
Es aquél Código QR que luce permanentemente igual ya que siempre
representa una misma cadena de texto. Este tipo de códigos son útiles cuando los parámetros asociados a los
códigos de cobro son siempre iguales. Dado que la representación gráfica no cambia, estos códigos pueden ser
impresos y usados múltiples veces para pagar un mismo código de cobro.
Referencia Cash in:
Es un código alfanumérico asociado a una transacción de pago en
efectivo, ya sea para realizar un depósito de efectivo, para realizar un pago de servicios, para realizar el
pago en efectivo de una orden de comercio electrónico, o cualquier otra operación equivalente. Las
referencias Cash In están siempre asociadas a parámetros específicos como Monto, Fecha, Hora, entre otros, y
son generadas a petición del usuario cuando este desea realizar una operación de este tipo.
Join the revolution.
Join now.