dapp
Pagos Moviles

Con la integración de pagos móviles tu comercio adquirirá la capacidad de generar códigos de cobro para ser desplegados en forma de Códigos QR dinámicos en el punto de venta. Estos serán escaneados por los usuarios de los medios de pago integrados al ecosistema de pagos móviles de dapp® desde el escáner de la cámara de sus teléfonos móviles o directamente desde el escáner de su billetera digital para, posteriormente, realizar el pago desde su dispositivo.

En esta documentación encontrarás toda la información necesaria para:

  • Generar códigos de cobro
  • Obtener notificación del pago

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.

Paso a Paso
Ambientes

En dapp® contamos 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/
Autenticación

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”

Ejemplo de código:
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);
Generar código de cobro

Para generar un Código QR de cobro, se debe consumir el siguiente endpoint:

/dapp-codes/

Url completa en sandbox:
https://sandbox.dapp.mx/v2/dapp-codes/

Método HTTP
POST

Tipo de API key requerida
Privada

Parámetros Aceptados

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

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



Ejemplos de código
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);
Ejemplo de respuesta:
{ "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ámetros devueltos:

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.


Obtener información de pago

El comercio debe exponer un servicio que acepte peticiones donde dapp® le enviará una notificación cuando el usuario haya realizado el pago.

Respuesta de ejemplo
{ "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/4Pq0QSLE9930ty4Pq0QSLE9930ty4+ESg74sNL VoiAbbHWKfxQjIViXCUG79wya9s/4ZW1cpN5ZdKXJ6HRs095SQrRAZzfQprMc/csZ3+d4qRwzwADfRfKCE52X311XJY5FnQcI2v/k Mx8H3oogMWzbb0PP0MFYmZb7/x2BtNMlog3KrRCQdHL2PgfvrBRLbQvKAmACIbb6yPIiKtMgj43nwJoz+pypFUPW3zsh5tOxTiwOmpBr B1HhhVhv0lO23qw73N1VrY1o2IIXrCWWDK1x4KW265r9SAo2/Fg==" } }
Parametros devueltos

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 hash
28e62e93-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

Ejemplos de firmas
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';
    }
}

Glosario

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.


Súmate a la revolución.
Intégrate ahora.

Súmate a la revolución.
Intégrate ahora.

Quiero Integrarme
Es la forma de contacto, ingresa un correo correcto
El número debe de ser de al menos 10 dígitos







Olvidé mi contraseña