Tokenizando cartão

Para garantir a segurança dos dados sensíveis do cartão, nenhuma informação de cartão deve trafegar ou ser armazenada em texto puro. Neste fluxo, os dados do cartão são tokenizados no cliente utilizando criptografia assimétrica, e apenas o token criptografado é enviado para a API.

Descrição

A plataforma suporta duas formas de tokenização:

1. Via script de tokenização (Front-end)recomendada para integrações diretas no browser, como checkouts web e aplicações client-side.
2. Via classe PaymentTokenizer (API / Back-end)recomendada para integrações server-to-server, onde a criptografia ocorre no backend do integrador antes do envio para a API.

Como Funciona a Tokenização?

O processo de tokenização segue os seguintes passos:

1. Utilize a chave de API da plataforma.
2. Os dados do cartão são criptografados localmente usando essa chave.
3. O resultado é um token.
4. Esse token é enviado para a API de pagamentos no lugar dos dados do cartão.

Forma 1 - Tokenização no Front-end (Script)

Para realizar transações com cartão de crédito, é necessário incluir o token do cartão na requisição.
A geração desse token é simples e deve ser feita no front-end, antes de enviar os dados para a API.
Abaixo está o primeiro passo, que consiste em importar o script oficial de tokenização.

Inclusão do Script de Tokenização

Adicione o script abaixo na sua página HTML ou antes de utilizar a função de tokenização:

HTML
<script src="https://api.diasmarketplace.com.br/v1/scripts"></script>

Exemplo de Uso — Gerando o Token do Cartão

NODE
const pk = "CHAVE_DE_API"

try {
  const card = {
    number: "5555444433331111",
    holderName: "John Doe",
    expMonth: 8,
    expYear: 2027,
    cvv: "789",
  };

  await DiasMarketPlace.init(pk);
  const token = await DiasMarketPlace.encrypt(card);
  console.log(token);
} catch (err) {
  console.error(err);
}

Forma 2 — Tokenização via API (Classe PaymentTokenizer)

Essa forma é indicada para quem vai integrar via backend (API, workers ou server-to-server) e precisa tokenizar o cartão antes de enviar para a API de pagamentos.
A tokenização continua acontecendo no seu ambiente, usando criptografia assimétrica. Apenas o token é enviado para a plataforma.

Funcionamento

1. Utilize a chave de API da plataforma.
2. Sua aplicação inicializa a classe PaymentTokenizer com essa chave.
3. Os dados do cartão são validados e criptografados localmente.
4. O resultado é um token.
5. Esse token é enviado para a API de pagamentos no lugar dos dados do cartão.

Dependência

A classe utiliza libsodium para criptografia:

NODE
import sodium from "https://cdn.jsdelivr.net/npm/libsodium-wrappers@0.8.0/+esm

Classe PaymentTokenizer

NODE
import sodium from "https://cdn.jsdelivr.net/npm/libsodium-wrappers@0.8.0/+esm";

type CardDTO = {
  number: string;
  holder: string;
  expMonth: string;
  expYear: string;
  cvv: string;
};


class PaymentTokenizer {
  publicKey = "";
  initialized = false;

  async init(pk: string): Promise<void> {
    this.assertValidPublicKey(pk);
    await sodium.ready;
    this.publicKey = pk;
    this.initialized = true;
  }

  async encrypt(card: CardDTO): Promise<string> {
    if (!this.initialized || !this.publicKey) {
      throw new Error("TOKENIZER_NOT_INITIALIZED");
    }

    const payload = this.buildPayload(card);

    const encrypted = sodium.crypto_box_seal(sodium.from_string(JSON.stringify(payload)), sodium.from_base64(this.publicKey));

    return sodium.to_base64(encrypted);
  }

  buildPayload(card: CardDTO): CardDTO {
    return {
      number: this.validateCardNumber(card.number),
      holder: this.validateHolder(card.holder),
      expMonth: this.validateMonth(card.expMonth),
      expYear: this.validateYear(card.expYear),
      cvv: this.validateCvv(card.cvv),
    };
  }

  assertValidPublicKey(pk: string) {
    if (!pk || typeof pk !== "string" || pk.trim() === "") {
      throw new Error("INVALID_PUBLIC_KEY");
    }
  }

  validateHolder(value: string): string {
    if (!value?.trim()) {
      throw new Error("INVALID_CARD_HOLDER");
    }
    return value.trim();
  }

  validateCardNumber(value: string): string {
    const number = String(value ?? "").replace(/\D/g, "");

    if (!/^\d{13,19}$/.test(number)) {
      throw new Error("INVALID_CARD_NUMBER");
    }

    return number;
  }

  validateMonth(value: string): string {
    const monthStr = String(value ?? "").replace(/\D/g, "");

    if (!/^\d{2}$/.test(monthStr)) {
      throw new Error("INVALID_EXP_MONTH");
    }

    const month = Number(monthStr);

    if (month < 1 || month > 12) {
      throw new Error("INVALID_EXP_MONTH");
    }

    return monthStr;
  }

  validateYear(value: string): string {
    const yearStr = String(value ?? "").replace(/\D/g, "");

    if (!/^\d{4}$/.test(yearStr)) {
      throw new Error("INVALID_EXP_YEAR");
    }

    const year = Number(yearStr);
    const currentYear = new Date().getFullYear();
    if (year < currentYear) {
      throw new Error("CARD_EXPIRED");
    }
    return yearStr;
  }

  validateCvv(value: string): string {
    const cvv = String(value ?? "").replace(/\D/g, "");

    if (!/^\d{3,4}$/.test(cvv)) {
      throw new Error("INVALID_CVV");
    }

    return cvv;
  }
}

Classe PaymentTokenizer

NODE
import { PaymentTokenizer } from "./PaymentTokenizer";

const pk = "SUA_CHAVE_DE_API";

async function tokenizeCard() {
  const tokenizer = new PaymentTokenizer();
  await tokenizer.init(pk);

  const token = await tokenizer.encrypt({
    number: "4111111111111111",
    holder: "JOHN DOE",
    expMonth: "12",
    expYear: "2030",
    cvv: "123"
  });

  console.log("Token do cartão:", token);
}

tokenizeCard();