JavaScript SDK

SDK 2.1.1

What it is#

The card fields live on your page. The V2 SDK takes over those inputs and sends the data from the browser straight to Tilopay: it never passes through your server.

Who it is for#

For teams that want to control the checkout design without card data touching their backend. You need a JavaScript frontend and a minimal backend to obtain the SDK token.

How to build it, in order#

  1. Credentials. There are no shared sandbox credentials: get yours through developer registration.
  2. SDK token. Your backend requests it with POST /api/v1/loginSdk. See authentication.
  3. Load the script. See installation.
  4. Add the inputs with the contract ids and the responseTilopay container. See form fields.
  5. Start the purchase with Tilopay.Init() and show the customer the payment methods it returns.
  6. Close the purchase with Tilopay.startPayment(). The SDK handles 3DS and renders the result on your redirect URL.
  7. Confirm the result on your server. The redirect belongs to the customer; before delivering the product, confirm the status with the API. See confirm the result.

Method flow#

SINPE Móvil: confirmation and timing#

When the transfer arrives, the SDK redirects to your redirect; Tilopay also notifies webhook if provided. The SDK does not set a deadline, but Tilopay matches transfers with SINPE Móvil orders created within the last 25 minutes: customers must transfer within that window. The session depends on the SDK token lifetime.

Required:    Init()  ──────────────────────────────►  startPayment()
Optional:            getCardType()   (show the brand icon)
                     updateOptions() (if data changes)

You can also accept Apple Pay: the SDK draws the button inside the form.

Confirm the result on your server#

When it finishes, the customer's browser lands on your redirect URL with the result parameters: code, description, auth, order, tpt, tilopay-transaction, OrderHash and returnData. See the full table in parameters that arrive at the redirect.

  • code = 1 means the transaction is approved; any other value is a decline.
  • The redirect is received by the customer's browser, so it is not enough proof: before delivering the product, confirm the status with POST /api/v1/consult from your server.
  • OrderHash is a unique string per transaction: its algorithm is not public and is requested by writing to soporte@tilopay.com.

Before assuming you are in test mode#

Testing and production share one host: the mode is verified with the environment field returned by Init() ("TEST" or "PROD"). See environments.

Full example#

Form inside .payFormTilopay, the five fields, Pay and Update buttons, responseTilopay outside the form, the v2 script, Init() with every field, loading methods and cards into their selects, and startPayment().

<div class="payFormTilopay">
  <select id="tlpy_payment_method" name="tlpy_payment_method"></select>
  <select id="tlpy_saved_cards" name="tlpy_saved_cards"></select>
  <input type="text" id="tlpy_cc_number" name="tlpy_cc_number" />
  <input type="text" id="tlpy_cc_expiration_date" name="tlpy_cc_expiration_date" placeholder="12/30" />
  <input type="text" id="tlpy_cvv" name="tlpy_cvv" />
  <button type="button" id="pagar">Pagar</button>
  <button type="button" id="actualizar">Actualizar</button>
</div>

<div id="responseTilopay"></div>

<script src="https://app.tilopay.com/sdk/v2/sdk_tpay.min.js"></script>
<script>
  async function iniciar() {
    const init = await Tilopay.Init({
      token: "<token_del_sdk>",
      currency: "CRC",
      language: "es",
      amount: 100.0,
      billToEmail: "cliente@ejemplo.com",
      orderNumber: "ORD-2026-000123",
      typeDni: 1,
      dni: "0707770777",
      billToFirstName: "Ana",
      billToLastName: "Rojas",
      billToAddress: "Avenida 1, Local 2",
      billToAddress2: "",
      billToCity: "San José",
      billToState: "SJ",
      billToZipPostCode: "10101",
      billToCountry: "CR",
      billToTelephone: "88888888",
      shipToFirstName: "Ana",
      shipToLastName: "Rojas",
      shipToAddress: "Avenida 1, Local 2",
      shipToAddress2: "",
      shipToCity: "San José",
      shipToState: "SJ",
      shipToZipPostCode: "10101",
      shipToCountry: "CR",
      shipToTelephone: "88888888",
      capture: 1,
      redirect: "https://ejemplo.com/respuesta",
      subscription: 0,
      hashVersion: "V2",
      returnData: "",
    });

    if (init.environment !== "TEST") console.warn("Init():", init.environment);

    const methods = document.getElementById("tlpy_payment_method");
    init.methods.forEach((m) => methods.add(new Option(m.name, m.id)));

    const cards = document.getElementById("tlpy_saved_cards");
    init.cards.forEach((c) => cards.add(new Option(c.name, c.id)));
  }

  document.getElementById("tlpy_cc_number").addEventListener("change", async () => {
    const brand = await Tilopay.getCardType();
    console.log(brand.message); // "visa", "mastercard", "amex"
  });

  document.getElementById("actualizar").addEventListener("click", async () => {
    await Tilopay.updateOptions({ billToTelephone: "87777777" });
  });

  document.getElementById("pagar").addEventListener("click", async () => {
    const payment = await Tilopay.startPayment();
    if (payment && payment.message) console.error(payment.message);
  });

  iniciar();
</script>

Compliance#

Card data does not pass through your server, but your page captures it: check the applicable scope with your compliance team.

Last verified: 2026-10-22

View as raw Markdown