Passa al contenuto principale
In questa pagina

Inizia con Nequi#

Questa pagina fornisce una guida completa per integrare con successo Nequi con DEUNA nel tuo checkout.

Nequi è un portafoglio digitale gestito da Bancolombia ed è ampiamente utilizzato in Colombia.

Come funziona#

L'integrazione di DEUNA con Nequi consente agli utenti di pagare attraverso un flusso di notifica push, completamente gestito all'interno del DEUNA Payment Widget, senza reindirizzamenti esterni richiesti.

Processo di pagamento

L'elenco seguente descrive un processo di pagamento valido con Nequi:

  1. L'utente seleziona Nequi all'interno del Payment Widget.
  2. L'utente entra nel proprio numero di telefono Nequi reale.
  3. Una notifica push viene inviata alla loro app mobile Nequi.
  4. L'utente approva il pagamento direttamente nell'app.
  5. DEUNA riceve lo stato di pagamento .
  6. DEUNA attiva l'evento di callback appropriato nel widget:
    • onSuccess per pagamenti approvati
    • onError per quelli rifiutati o scaduti

sulla gestione degli eventi di Error

Il widget DEUNA attiva il onError callback nei seguenti scenari:

  1. Il numero di telefono fornito nel modulo di checkout è valido ma non associato a un account Nequi.
  2. L'utente rifiuta la notifica push dall'app Nequi.

In entrambi i casi, DEUNA restituisce una risposta, tra cui un codice di errore e un messaggio di errore che descrive il fallimento. Puoi gestire la risposta in base alla logica aziendale e all'esperienza utente desiderata.

Requisiti#

I seguenti contenuti elencano tutti i requisiti per una corretta integrazione con Nequi.

Le credenziali di produzione e sandbox Nequi devono essere richieste direttamente dal gestore del tuo account Bancolombia o Nequi.

Ambienti:

Completa il processo di certificazione con Nequi e ottieni le seguenti credenziali fornite da Nequi:

  • Merchant ID

  • Password di Merchant

  • Public Key.

  • Codice di identificazione

  • Tempo di scadenza (max. 45min)

Configura i webhook in Nequi#

Configura i webhooks nella tua sessione Nequi per ogni negozio.

Se non si configurano i webhook Nequi, allora gli aggiornamenti di stato in Nequi si lagranno tra due a cinque minuti.

Assicurarsi che il vostro endpoint webhook è altamente disponibile e scalabile. Opzionalmente, è possibile configurare l'inversione di transazione se la conferma di pagamento non viene eseguita a causa di un servizio indisponibile.

Webhook Payload

Nequi invierà il seguente carico di pagamento JSON:

JSON
{
  "commerceCode": "29603",
  "value": "1",
  "phoneNumber": "3195414070",
  "messageId": "60396545535",
  "transactionId": "350-12345-34000201-60396545535",
  "region": "C001",
  "receivedAt": "2023-02-27T15:50:13.527Z",
  "paymentStatus": "SUCCESS"
}

Campi di carico

CampoDescrizione
commerceCodeIl tuo codice commerciale interno Nequi
valueImporto di pagamento
phoneNumberNumero di telefono cellulare del Payer
messageIdIdentificatore di transazione unico
transactionIdIdentificativo di pagamento
regionRegione di pagamento: P001 (Panama) o C001 (Colombia)
receivedAttimestamp di pagamento in formato JSON
paymentStatusStato di pagamento: SUCCESS, CANCELEDo DENIED

Verifica della richiesta di sicurezza

Tutte le richieste di webhook da Nequi includono intestazioni di sicurezza che è necessario verificare:

Richiesta intestazioni

JSON
{
  "Content-Type": "application/json",
  "Digest": "SHA-256=43GpOk5L54gfpAMBE0xNX1bj2hJA9JJ1RR0dErHfZhI=",
  "Signature": "keyId=\"YourClientId\",algorithm=\"hmac-sha384\",headers=\"content-type digest\",signature=\"...\""
}

Processo di verifica

È necessario verificare due cose per ogni richiesta:

1. Verificare il digerimento del corpo

Il Digest intestazione contiene un hash SHA-256 del corpo di richiesta:

JavaScript
// Convert body to string
const parsedBody = JSON.stringify(request.body);

// Calculate SHA-256 hash
const calculatedDigest = `SHA-256=${createHash('sha256')
  .update(parsedBody)
  .digest('base64')}`;

// Verify it matches the header
if (calculatedDigest !== request.headers.Digest) {
  return 401; // Invalid Digest
}
2. Verificare la firma della richiesta

Il Signature l'intestazione assicura che la richiesta provenga da Nequi:

  1. Parsare l'intestazione della firma.
JavaScript
const parts = request.headers.Signature.split(',');
const signature = {};

for (const part of parts) {
  const [key, value] = part.split('=');
  signature[key] = value.slice(1, -1); // Remove quotes
}

// Result:
// {
//   keyId: "YourClientId",
//   algorithm: "hmac-sha384",
//   headers: "content-type digest",
//   signature: "..."
// }
  1. Costruisci il testo della firma.
JavaScript
const headerNames = signature.headers.split(' '); // ["content-type", "digest"]
const linesForSignature = [];

for (const headerName of headerNames) {
  const value = request.headers[headerName.toLowerCase()];
  linesForSignature.push(`${headerName}: ${value}`);
}

const textForSignature = linesForSignature.join('\n');
// Result:
// "content-type: application/json
// digest: SHA-256=43GpOk5L54gfpAMBE0xNX1bj2hJA9JJ1RR0dErHfZhI="
  1. Calcola e verifica HMAC.
JavaScript
const APP_SECRET = 'YourSharedSecret'; // Store securely, never hardcode

// Calculate HMAC-SHA384
const base64hmac = createHmac('sha384', APP_SECRET)
  .update(textForSignature)
  .digest('base64');

// Convert to base64url format
const calculatedSignature = base64hmac
  .replace(/\+/g, '-')
  .replace(/\//g, '_')
  .replace(/=+$/, '');

// Verify signature
if (calculatedSignature !== signature.signature) {
  return 401; // Invalid Signature
}

Esempio di applicazione completo (Node.js)

JavaScript
const { createHash, createHmac } = require('node:crypto');

// Store securely - use environment variables
const APP_SECRET = process.env.NEQUI_WEBHOOK_SECRET;

async function handleNequiWebhook(request, response) {
  try {
    // 1. Verify Body Digest
    const parsedBody = JSON.stringify(request.body);
    const calculatedDigest = `SHA-256=${createHash('sha256')
      .update(parsedBody)
      .digest('base64')}`;
    
    if (calculatedDigest !== request.headers.digest) {
      return response.status(401).send('Invalid Digest');
    }

    // 2. Parse Signature Header
    const parts = request.headers.signature.split(',');
    const signatureData = {};
    
    for (const part of parts) {
      const [key, value] = part.split('=');
      signatureData[key] = value.slice(1, -1);
    }

    // 3. Build Signing Text
    const headerNames = signatureData.headers.split(' ');
    const linesForSignature = headerNames.map(name => 
      `${name}: ${request.headers[name.toLowerCase()]}`
    );
    const textForSignature = linesForSignature.join('\n');

    // 4. Verify Signature
    const base64hmac = createHmac('sha384', APP_SECRET)
      .update(textForSignature)
      .digest('base64');
    
    const calculatedSignature = base64hmac
      .replace(/\+/g, '-')
      .replace(/\//g, '_')
      .replace(/=+$/, '');

    if (calculatedSignature !== signatureData.signature) {
      return response.status(401).send('Invalid Signature');
    }

    // 5. Process the payment notification asynchronously
    processPaymentAsync(request.body);

    // 6. Respond immediately (within 10 seconds)
    return response.status(200).send('OK');

  } catch (error) {
    console.error('Webhook processing error:', error);
    return response.status(500).send('Internal Server Error');
  }
}

async function processPaymentAsync(paymentData) {
  // Process payment in background
  // Update your database, trigger notifications, etc.
  
  const { transactionId, paymentStatus, value, phoneNumber } = paymentData;
  
  if (paymentStatus === 'SUCCESS') {
    // Handle successful payment
  } else if (paymentStatus === 'CANCELED' || paymentStatus === 'REFUSED') {
    // Handle failed payment
  }
}

Integrare Nequi#

Ora che i requisiti tecnici sono impostati, è possibile avviare l'integrazione passo-passo.

1. Creare un order_token

Per effettuare un acquisto, è necessario creare un ordine DEUNA.

Fai una richiesta al Creare un Ordine endpoint.

L'API restituisce un order_token, che viene utilizzato durante tutto il flusso.

2. Prendi il order_token

Fai una richiesta al Ordinare endpoint.

Utilizzare questo token per i prossimi passi.

3. Render il widget di pagamento

Dopo aver ricevuto il gettone dell'ordine, è possibile rendere il widget DEUNA.

JavaScript
await DeunaSDK.initPaymentWidget({
    orderToken: "<DEUNA order token>",
    callbacks: ...,
    paymentMethods: [
        {
            paymentMethod: "voucher",
            processors: ["nequi_push_voucher"],
        },
    ],
});

4. Fare un pagamento della carta

Fai una richiesta di pagamento della carta al Acquisto V2 endpoint e processare il pagamento.

Restituisce l’ordine elaborato con

Card payment exampleJavaScript
{
  "order": {
    "cash_change": 0,
    "currency": "USD",
    "discounts": [],
    "display_items_total_amount": "",
    "display_shipping_amount": "",
    "display_sub_total": "",
    "display_tax_amount": "",
    "display_total_amount": "",
    "display_total_discount": "",
    "gift_card": [],
    "items": [
      {
        "brand": "",
        "category": "",
        "color": "",
        "description": "Papa Fritas",
        "details_url": "",
        "discounts": [],
        "id": "001",
        "image_url": "https://images-staging.getduna.com/95463fb5-6279-4ec3-8ff9-fe07aacd2142/cd928351d12c6b96_domicilio_316_744x744.png?d=600x600",
        "isbn": "",
        "manufacturer": "",
        "name": "Papa Fritas",
        "options": "",
        "quantity": 1,
        "size": "",
        "sku": "",
        "tax_amount": {
          "amount": 44,
          "currency": "USD",
          "currency_symbol": "$",
          "display_amount": ""
        },
        "taxable": false,
        "total_amount": {
          "amount": 594,
          "currency": "USD",
          "currency_symbol": "$",
          "display_amount": "",
          "display_original_amount": "",
          "display_total_discount": "",
          "original_amount": 0,
          "total_discount": 0
        },
        "type": "",
        "unit_price": {
          "amount": 550,
          "currency": "USD",
          "currency_symbol": "$",
          "display_amount": ""
        },
        "uom": "",
        "upc": "",
        "weight": {
          "unit": "",
          "weight": 0
        }
      },
      {
        "brand": "",
        "category": "",
        "color": "",
        "description": "Hamburguesa ",
        "details_url": "",
        "discounts": [],
        "id": "002",
        "image_url": "https://images-staging.getduna.com/95463fb5-6279-4ec3-8ff9-fe07aacd2142/cd928351d12c6b96_domicilio_51330_744x744_1646338877.png?d=600x600",
        "isbn": "",
        "manufacturer": "",
        "name": "Hamburguesa",
        "options": "",
        "quantity": 2,
        "size": "",
        "sku": "",
        "tax_amount": {
          "amount": 88,
          "currency": "USD",
          "currency_symbol": "$",
          "display_amount": ""
        },
        "taxable": false,
        "total_amount": {
          "amount": 3088,
          "currency": "USD",
          "currency_symbol": "$",
          "display_amount": "",
          "display_original_amount": "",
          "display_total_discount": "",
          "original_amount": 0,
          "total_discount": 0
        },
        "type": "",
        "unit_price": {
          "amount": 1500,
          "currency": "USD",
          "currency_symbol": "$",
          "display_amount": ""
        },
        "uom": "",
        "upc": "",
        "weight": {
          "unit": "",
          "weight": 0
        }
      }
    ],
    "items_total_amount": 3682,
    "metadata": {
      "key1": "NO REQUERIDO",
      "key2": "NO REQUERIDO"
    },
    "order_id": "order116",
    "payment": {
      "data": {
        "amount": {
          "amount": 3770,
          "currency": "USD"
        },
        "authorization_3ds": {
          "html_content": "<div></div>",
          "url_challenge": "",
          "version": "1.1.1"
        },
        "authorization_code": "TEST00",
        "created_at": "2022-07-22 20:22:48.408419489 +0000 UTC",
        "customer": {
          "email": "jhondoe@deuna.com",
          "id": "xxxxxx-0eb3-450f-8b26-4ff23208470f"
        },
        "from_card": {
          "card_brand": "Visa",
          "first_six": "411111",
          "last_four": "1111"
        },
        "id": "order116",
        "installments": {
          "installment_amount": 5999,
          "installment_rate": 0.12,
          "installment_type": "MCI",
          "installments": 3,
          "plan_id": "7471ed27-094d-44c4-a62d-225644b782f7",
          "plan_option_id": "87309ea8-3942-4fdf-95ec-ce29a792aff2"
        },
        "merchant": {
          "id": "9a85e296-cc3d-454b-b591-208d6e538126",
          "store_code": "all"
        },
        "metadata": {
          "authorization_code": "TEST00"
        },
        "method_type": "credit_card",
        "processor": "paymentez",
        "reason": "",
        "status": "processed",
        "updated_at": "2022-07-22 20:22:48.408809765 +0000 UTC"
      }
    },
    "redirect_url": "",
    "scheduled_at": "",
    "shipping": null,
    "shipping_address": {
      "additional_description": "Piso 9",
      "address_type": "home",
      "address1": "Av. de los Incas 15-33, Ambato 180202, Ecuador",
      "address2": "Av. de los Incas 15-33, Ambato 180202, Ecuador",
      "city": "Ambato",
      "country_code": "EC",
      "created_at": "0001-01-01T00:00:00Z",
      "first_name": "Jhon",
      "id": 0,
      "identity_document": "146565656",
      "is_default": true,
      "last_name": "Doe",
      "lat": -1.2480678792202227,
      "lng": -78.62532788804577,
      "phone": " 946565665",
      "state_name": "Tungurahua",
      "updated_at": "0001-01-01T00:00:00Z",
      "user_id": "xxxxx4e2-xxxx-xxxx-xxxx-xxxxx5b7b2e",
      "zipcode": "180202"
    },
    "shipping_amount": 0,
    "shipping_method": null,
    "shipping_methods": [],
    "shipping_options": {},
    "status": "succeeded",
    "store_code": "",
    "sub_total": 3550,
    "tax_amount": 132,
    "timezone": "",
    "total_amount": 3770,
    "total_discount": 0,
    "user_instructions": "Piso 9",
    "webhook_urls": null
  },
  "order_token": "0b98dbe8-d265-49bc-b80d-536cea46509c"
}

Test Nequi#

Per testare Nequi, è necessario richiedere i dati di prova per Nequi. Se necessario, il pagamento può essere rimborsato tramite il Pannello di amministrazione DEUNA o tramite il Rimborsi API.