Centro desarrolladores

Centro de Desarrolladores

Somos una entidad financiera diferente, más cercana y especializada. Llevamos desde 2004 dando soluciones personalizadas y diseñadas por sector y tipo de cliente, claras, sencillas y seguras. Cuenta con nuestra experiencia gestionando créditos y fraccionando pagos, sabemos lo que necesitas.
  • PLUGINS
  • WEB CHECKOUT · INTEGRACIÓN B2B
  • INSTALACIÓN POR AGENTE IA

Instrucciones

Conectarte con Frakmenta es muy fácil si utilizas una de las principales plataformas del mercado. Elige tu plataforma para descargar el plugin o solicitar la instalación:

👉 Enfocado a comercios que usan PrestaShop, WooCommerce o Shopify: instalas el plugin y listo, sin programar.

  • Selecciona la plataforma de tu tienda online.
  • Elige la versión de tu plataforma, si aplica.
  • Descarga el plugin y la documentación, o contacta con nosotros para completar la instalación.

PRESTASHOP


Documentación Plugin

WOOCOMMERCE


Documentación Plugin

SHOPIFY

Para instalar el plugin de Shopify, contacta con nosotros para que podamos generar el enlace de instalación personalizado para tu comercio.

Contactar Documentación

Instrucciones

👉 Enfocada a desarrollos a medida: es la parte de servidor de la integración nativa. Tu backend firma y crea la operación contra la API de Frakmenta, en el lenguaje que uses, con máximo control.

  • El navegador muestra el simulador de cuotas con tu clave pública.
  • Tu backend firma y crea la operación: POST /api/fk/v2/operations.
  • Frakmenta devuelve un token; tu web lo envía por POST a /op/ecommerce/load sobre el iframe.
  • Confirmas el pedido por la notificación server-to-server, no solo por la success_url.
La firma de cada operación es sha256( merchant_id | delegation | "e-commerce" | invoice_id | product_price | "EUR" | private_key ), con product_price en céntimos. La clave privada nunca sale del backend.

Ejemplos por lenguaje:

El panel Frontend es el mismo para todos; el resto crean la operación en tu backend y devuelven el token. Prueba primero en beta2.frakmenta.com con la tarjeta de test 4548 8144 7972 7229.
<!-- Flujo OFICIAL del widget (widgetEcommerce.js). El widget hace el checkout;
     tú solo aportas el HTML con estos ids EXACTOS y un endpoint que devuelva el token. -->

<!-- 1) En el <head>: estilos y widget oficial (la clave PÚBLICA sí va en el navegador) -->
<link rel="stylesheet" href="https://beta2.frakmenta.com/css/widget-ecommerce.css">
<script defer
  src="https://beta2.frakmenta.com/js/widgetEcommerce.js"
  data-name="widgetFK"
  data-api-url="https://beta2.frakmenta.com"
  data-apikey="TU_CLAVE_PUBLICA"></script>

<!-- 2) Simulador de cuotas (data-product_price en CÉNTIMOS). Lo rellena el widget. -->
<div id="fk-widget-installments" data-product_price="60000"></div>

<!-- 3) Botón de pago. El widget le pone el texto y le engancha el click.
        data-url = TU endpoint (GET) que crea la operación en tu backend y devuelve el token en JSON. -->
<button id="Pagar" type="button" data-url="https://tu-tienda.com/api/frakmenta/token"></button>

<!-- 4) Contenedor del checkout con los ids EXACTOS que busca el widget:
        #modalFK    -> el widget lo muestra al pulsar Pagar
        #loaderDiv  -> tu loader (el widget lo muestra/oculta)
        #fk-form-installments -> form: action = {BASE}/op/ecommerce/load, target = name del iframe
                                 (el widget añade solo el input fk-lang)
        #token      -> input oculto donde el widget escribe el token
        #frakmentaEcommerce -> el iframe; el widget LO LOCALIZA POR ESTE id (no por la clase) -->
<div id="modalFK" style="display:none">
  <div id="loaderDiv" style="display:none">Cargando…</div>
  <form id="fk-form-installments" method="POST"
        action="https://beta2.frakmenta.com/op/ecommerce/load" target="frameEcommerce">
    <input type="hidden" id="token" name="token" value="">
  </form>
  <iframe class="iframe-fk" id="frakmentaEcommerce" name="frameEcommerce" scrolling="no"
          style="width:100%;height:640px;border:0;display:none"></iframe>
</div>

<!-- 5) Inicializa el widget cuando el script haya cargado -->
<script>
  (function espera(n){
    if (typeof window.simulator === 'function') return window.simulator();
    if (n < 30) setTimeout(function(){ espera(n + 1); }, 200);
  })(0);
</script>

<!-- Qué hace el widget al pulsar #Pagar (initModalPay en widgetEcommerce.js):
     1. fetch(data-url)  (GET)  ->  2. token = response.json()
     3. document.getElementById("token").value = token
     4. document.getElementById("fk-form-installments").submit()   // POST a /op/ecommerce/load con token + fk-lang
     5. muestra #frakmentaEcommerce
     Por eso tu endpoint data-url debe devolver el TOKEN como JSON (no un objeto {token: ...}). -->
<?php
// backend/crear-operacion.php  —  responde el token a tu frontend
$BASE       = 'https://beta2.frakmenta.com';
$merchantId = 30100;
$delegation = '1';
$privateKey = getenv('FRAKMENTA_PRIVATE_KEY'); // NUNCA en el frontend

$invoiceId    = 'PEDIDO-' . date('Ymd-His') . '-' . bin2hex(random_bytes(4));
$productPrice = 60000; // en céntimos

// 1) Firma SHA-256 (orden EXACTO de los 7 campos, unidos por "|")
$signature = hash('sha256', implode('|', [
    $merchantId, $delegation, 'e-commerce',
    $invoiceId, $productPrice, 'EUR', $privateKey,
]));

// 2) Payload (rellena customer/order con los datos reales del pedido)
$payload = [
    'merchant_id'   => $merchantId,
    'invoice_id'    => $invoiceId,
    'product_price' => $productPrice,
    'currency_code' => 'EUR',
    'delegation'    => $delegation,
    'type'          => 'e-commerce',
    'customer'      => [ /* identification, address, ... */ ],
    'order'         => [ 'id' => $invoiceId, 'products' => [ /* ... */ ] ],
    'flow_config'   => [
        'success_url'      => "https://tu-tienda.com/ok?invoice=$invoiceId",
        'notification_url' => 'https://tu-tienda.com/api/frakmenta/notify',
        'ko_url'           => "https://tu-tienda.com/ko?invoice=$invoiceId",
    ],
    'signature'     => $signature,
];

// 3) Llamada a la API v2
$ch = curl_init("$BASE/api/fk/v2/operations");
curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json', 'Accept: application/json'],
    CURLOPT_POSTFIELDS     => json_encode($payload),
    CURLOPT_RETURNTRANSFER => true,
]);
$res = json_decode(curl_exec($ch), true);
curl_close($ch);

// 4) Extrae el token y devuélvelo a tu frontend
$token = $res['token'] ?? $res['data']['token_url'] ?? null;
header('Content-Type: application/json');
echo json_encode(['token' => $token, 'invoice_id' => $invoiceId]);
// backend/frakmenta.js  —  Node 20+ (fetch nativo)
import crypto from 'node:crypto';

const BASE       = 'https://beta2.frakmenta.com';
const merchantId = 30100;
const delegation = '1';
const privateKey = process.env.FRAKMENTA_PRIVATE_KEY; // NUNCA en el frontend

export async function crearOperacion() {
  const invoiceId    = `PEDIDO-${Date.now()}-${crypto.randomBytes(4).toString('hex')}`;
  const productPrice = 60000; // en céntimos

  // 1) Firma SHA-256 (orden EXACTO de los 7 campos, unidos por "|")
  const signature = crypto.createHash('sha256').update(
    [merchantId, delegation, 'e-commerce', invoiceId, productPrice, 'EUR', privateKey].join('|'),
  ).digest('hex');

  // 2) Payload (rellena customer/order con los datos reales del pedido)
  const payload = {
    merchant_id: merchantId, invoice_id: invoiceId, product_price: productPrice,
    currency_code: 'EUR', delegation, type: 'e-commerce',
    customer: { /* identification, address, ... */ },
    order: { id: invoiceId, products: [ /* ... */ ] },
    flow_config: {
      success_url: `https://tu-tienda.com/ok?invoice=${invoiceId}`,
      notification_url: 'https://tu-tienda.com/api/frakmenta/notify',
      ko_url: `https://tu-tienda.com/ko?invoice=${invoiceId}`,
    },
    signature,
  };

  // 3) Llamada a la API v2
  const res  = await fetch(`${BASE}/api/fk/v2/operations`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
    body: JSON.stringify(payload),
  });
  const data = await res.json();

  // 4) Extrae el token y devuélvelo a tu frontend
  const token = data.token ?? data?.data?.token_url ?? null;
  return { token, invoice_id: invoiceId };
}
# backend/frakmenta.py  —  requiere: pip install requests
import os, hashlib, secrets
from datetime import datetime
import requests

BASE        = "https://beta2.frakmenta.com"
MERCHANT_ID = 30100
DELEGATION  = "1"
PRIVATE_KEY = os.environ["FRAKMENTA_PRIVATE_KEY"]  # NUNCA en el frontend

def crear_operacion():
    invoice_id    = f"PEDIDO-{datetime.now():%Y%m%d-%H%M%S}-{secrets.token_hex(4)}"
    product_price = 60000  # en céntimos

    # 1) Firma SHA-256 (orden EXACTO de los 7 campos, unidos por "|")
    firma = hashlib.sha256("|".join([
        str(MERCHANT_ID), DELEGATION, "e-commerce",
        invoice_id, str(product_price), "EUR", PRIVATE_KEY,
    ]).encode()).hexdigest()

    # 2) Payload (rellena customer/order con los datos reales del pedido)
    payload = {
        "merchant_id": MERCHANT_ID, "invoice_id": invoice_id,
        "product_price": product_price, "currency_code": "EUR",
        "delegation": DELEGATION, "type": "e-commerce",
        "customer": {},  # identification, address, ...
        "order": {"id": invoice_id, "products": []},
        "flow_config": {
            "success_url": f"https://tu-tienda.com/ok?invoice={invoice_id}",
            "notification_url": "https://tu-tienda.com/api/frakmenta/notify",
            "ko_url": f"https://tu-tienda.com/ko?invoice={invoice_id}",
        },
        "signature": firma,
    }

    # 3) Llamada a la API v2
    r = requests.post(f"{BASE}/api/fk/v2/operations", json=payload, timeout=30)
    data = r.json()

    # 4) Extrae el token y devuélvelo a tu frontend
    token = data.get("token") or data.get("data", {}).get("token_url")
    return {"token": token, "invoice_id": invoice_id}
# Referencia neutra de la API. La firma se calcula en tu backend, nunca en cliente.

# 1) Firma SHA-256 de los 7 campos unidos por "|" (ejemplo con openssl)
FIRMA=$(printf '%s' "30100|1|e-commerce|PEDIDO-123|60000|EUR|$FRAKMENTA_PRIVATE_KEY" \
  | openssl dgst -sha256 -r | awk '{print $1}')

# 2) Crear la operación (product_price en céntimos)
curl -sS -X POST "https://beta2.frakmenta.com/api/fk/v2/operations" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "merchant_id": 30100,
    "invoice_id": "PEDIDO-123",
    "product_price": 60000,
    "currency_code": "EUR",
    "delegation": "1",
    "type": "e-commerce",
    "customer": {},
    "order": { "id": "PEDIDO-123", "products": [] },
    "flow_config": {
      "success_url": "https://tu-tienda.com/ok?invoice=PEDIDO-123",
      "notification_url": "https://tu-tienda.com/api/frakmenta/notify",
      "ko_url": "https://tu-tienda.com/ko?invoice=PEDIDO-123"
    },
    "signature": "'"$FIRMA"'"
  }'

# La respuesta JSON incluye el token (campo "token" o "data.token_url").
# Después, el navegador hace POST de ese token a /op/ecommerce/load (ver pestaña Frontend).
// Frakmenta.cs  —  .NET 6+  (System.Net.Http / System.Text.Json)
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;

const string BASE = "https://beta2.frakmenta.com";
const int    merchantId = 30100;
const string delegation = "1";
string privateKey = Environment.GetEnvironmentVariable("FRAKMENTA_PRIVATE_KEY")!; // NUNCA en cliente

string invoiceId    = $"PEDIDO-{DateTime.Now:yyyyMMdd-HHmmss}-{Guid.NewGuid():N}";
int    productPrice = 60000; // en céntimos

// 1) Firma SHA-256 (orden EXACTO de los 7 campos, unidos por "|")
string origen = string.Join("|", merchantId, delegation, "e-commerce",
                                 invoiceId, productPrice, "EUR", privateKey);
string signature = Convert.ToHexString(
    SHA256.HashData(Encoding.UTF8.GetBytes(origen))).ToLowerInvariant();

// 2) Payload (rellena customer/order con los datos reales del pedido)
var payload = new {
    merchant_id = merchantId, invoice_id = invoiceId, product_price = productPrice,
    currency_code = "EUR", delegation, type = "e-commerce",
    customer = new { },
    order = new { id = invoiceId, products = Array.Empty<object>() },
    flow_config = new {
        success_url      = $"https://tu-tienda.com/ok?invoice={invoiceId}",
        notification_url = "https://tu-tienda.com/api/frakmenta/notify",
        ko_url           = $"https://tu-tienda.com/ko?invoice={invoiceId}"
    },
    signature
};

// 3) Llamada a la API v2
using var http = new HttpClient();
var body = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json");
var res  = await http.PostAsync($"{BASE}/api/fk/v2/operations", body);

// 4) Extrae el token y devuélvelo a tu frontend
using var doc = JsonDocument.Parse(await res.Content.ReadAsStringAsync());
string? token = doc.RootElement.TryGetProperty("token", out var t)
    ? t.GetString()
    : doc.RootElement.GetProperty("data").GetProperty("token_url").GetString();
// Frakmenta.java  —  Java 17+  (java.net.http)
import java.net.URI;
import java.net.http.*;
import java.security.MessageDigest;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

String BASE       = "https://beta2.frakmenta.com";
int    merchantId = 30100;
String delegation = "1";
String privateKey = System.getenv("FRAKMENTA_PRIVATE_KEY"); // NUNCA en cliente

String invoiceId    = "PEDIDO-" +
    LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd-HHmmss"));
int    productPrice = 60000; // en céntimos

// 1) Firma SHA-256 (orden EXACTO de los 7 campos, unidos por "|")
String origen = String.join("|", String.valueOf(merchantId), delegation, "e-commerce",
        invoiceId, String.valueOf(productPrice), "EUR", privateKey);
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] hash = md.digest(origen.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte b : hash) sb.append(String.format("%02x", b));
String signature = sb.toString();

// 2) Payload (usa tu librería JSON para construirlo con los datos reales del pedido)
String payload = """
  { "merchant_id": %d, "invoice_id": "%s", "product_price": %d,
    "currency_code": "EUR", "delegation": "%s", "type": "e-commerce",
    "customer": {}, "order": { "id": "%s", "products": [] },
    "flow_config": {
      "success_url": "https://tu-tienda.com/ok?invoice=%s",
      "notification_url": "https://tu-tienda.com/api/frakmenta/notify",
      "ko_url": "https://tu-tienda.com/ko?invoice=%s" },
    "signature": "%s" }
  """.formatted(merchantId, invoiceId, productPrice, delegation,
                invoiceId, invoiceId, invoiceId, signature);

// 3) Llamada a la API v2
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder(URI.create(BASE + "/api/fk/v2/operations"))
    .header("Content-Type", "application/json")
    .header("Accept", "application/json")
    .POST(HttpRequest.BodyPublishers.ofString(payload))
    .build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

// 4) Extrae el token de response.body() ("token" o "data.token_url") y devuélvelo a tu frontend
Consulta la documentación técnica completa de la API.

Instrucciones

👉 Enfocada a equipos que usan agentes de código (IA). Integra el pago fraccionado de Frakmenta con tu agente (Claude Code, Cursor, Copilot, Windsurf…): pásale el prompt, dale las claves de tu Área Comercio y realizará la integración de extremo a extremo, sin intervención.

  • Copia el prompt completo: ya incluye la guía de integración que el agente debe seguir.
  • Pégalo en tu agente sobre el proyecto de tu tienda.
  • Facilítale tus claves cuando te las pida y valida en Pruebas antes de pasar a Producción.
Compatible con Claude Code, Cursor, GitHub Copilot, Windsurf y cualquier agente con acceso a ficheros.

Prompt para tu agente (incluye el documento):

Un solo bloque: pulsa Copiar y pégalo en tu agente. Ya lleva dentro el prompt y la guía de integración completa, así que tiene todo lo necesario sin ensamblar nada.
Integra el pago fraccionado de Frakmenta (Web Checkout B2B) en este proyecto.

Sigue de forma literal la "Guía de integración para agentes" que te pego a
continuación (está publicada en el portal de desarrolladores de Frakmenta,
pestaña «Instalación por agente»). No inventes endpoints, campos ni la fórmula
de firma: usa exactamente los de esa guía.

================================================================
 GUÍA DE INTEGRACIÓN (documento que debes seguir literalmente)
================================================================

# Frakmenta · Web Checkout B2B — Guía de integración para agentes

> Documento autocontenido para que un **agente de código** (Claude Code, Cursor,
> GitHub Copilot, etc.) integre el **pago fraccionado de Frakmenta** en un
> comercio de extremo a extremo, **sin intervención humana**.
>
> Si eres un agente: lee este documento entero antes de escribir código. No
> inventes endpoints, campos ni fórmulas de firma: usa exactamente los que
> aparecen aquí. Cuando falte una credencial o una URL pública, **detente y
> pídesela al usuario**; no la inventes ni la dejes en blanco.

---

## 1. Qué vas a construir

Frakmenta se integra en tres piezas:

1. **Simulador de cuotas** (frontend): un widget oficial que muestra al cliente
   en cuántas cuotas puede pagar. Solo necesita la **clave pública**.
2. **Creación de la operación** (backend): tu servidor firma los datos del
   pedido con la **clave privada** y llama a la API de Frakmenta, que devuelve un
   `token`.
3. **Checkout** (frontend): tu web envía ese `token` por `POST` a Frakmenta y
   muestra el flujo de pago dentro de un `iframe`.

```text
Navegador (widget + iframe)  ->  Tu backend (firma)  ->  API Frakmenta  ->  token  ->  iframe checkout
```

**Regla de oro de seguridad:** la **clave privada** y la **firma** viven SOLO en
el backend. Nunca en el navegador, nunca en una variable `VITE_*`/`NEXT_PUBLIC_*`,
nunca en el repositorio.

---

## 2. Credenciales y entornos

El comercio obtiene sus credenciales en el **Área Comercio** (sección «claves
ecommerce»). Son cuatro valores:

| Valor            | Dónde va      | Descripción                                  |
| ---------------- | ------------- | -------------------------------------------- |
| `merchant_id`    | backend       | Identificador numérico del comercio.         |
| `delegation`     | backend       | Delegación (normalmente `"1"`).              |
| Clave pública    | **frontend**  | Inicializa el widget del simulador.          |
| Clave privada    | backend       | Firma cada operación. **Secreta.**           |

Entornos (base URL):

| Entorno    | Base URL                        |
| ---------- | ------------------------------- |
| Pruebas    | `https://beta2.frakmenta.com`   |
| Producción | `https://frakmenta.com`         |

Integra y valida **siempre primero en Pruebas**. Tarjeta de test:
`4548 8144 7972 7229` (caducidad y CVV cualesquiera).

Guarda las credenciales en variables de entorno del backend, por ejemplo:

```dotenv
FRAKMENTA_API_URL=https://beta2.frakmenta.com
FRAKMENTA_MERCHANT_ID=30100
FRAKMENTA_DELEGATION=1
FRAKMENTA_PRIVATE_KEY=__pídela_al_usuario__
FRONTEND_PUBLIC_URL=https://tu-tienda.com
BACKEND_PUBLIC_URL=https://tu-tienda.com
```

---

## 3. La firma (imprescindible)

Cada operación se firma con **SHA-256** sobre la concatenación de 7 campos
unidos por el carácter `|` (barra vertical), **en este orden exacto**:

```text
merchant_id | delegation | "e-commerce" | invoice_id | product_price | "EUR" | private_key
```

- `product_price` es un **entero en céntimos** (65,70 € → `6570`).
- El literal del tipo es `e-commerce` y la moneda `EUR`.
- El resultado es el hash en **hexadecimal en minúsculas**.

Vector de prueba (para autovalidar tu implementación):

```text
merchant_id   = 30100
delegation    = "1"
tipo          = "e-commerce"
invoice_id    = "frakmenta123"
product_price = 60000
currency      = "EUR"
private_key   = <clave privada de pruebas>

cadena  = "30100|1|e-commerce|frakmenta123|60000|EUR|<clave privada>"
sha256  = a6766940a68e13e16aec8c32ea6b096ee18051685a35ac0691450b14d0700964
```

Si con la clave privada de pruebas del comercio obtienes ese hash, la fórmula es
correcta. (El hash de arriba corresponde a la clave privada de ejemplo del kit
de Frakmenta; con otra clave el hash será distinto.)

---

## 4. Crear la operación (backend → API)

`POST {BASE}/api/fk/v2/operations`
`Content-Type: application/json`

Cuerpo (rellena con datos reales del pedido; los campos de cliente/dirección son
los datos de facturación del comprador):

```json
{
  "merchant_id": 30100,
  "invoice_id": "PEDIDO-2026-000123",
  "product_price": 60000,
  "currency_code": "EUR",
  "delegation": "1",
  "type": "e-commerce",
  "customer": {
    "identification": {
      "nif": "99999999R",
      "legal_first_name": "Nombre",
      "legal_last_name": "Apellidos",
      "date_of_birth": "1990-01-30",
      "mobile_phone_number": "600000000",
      "email": "cliente@ejemplo.com"
    },
    "address": {
      "line_1": "Calle Ejemplo 1", "line_2": " ", "phone": "600000000",
      "city": "Madrid", "state": "Madrid", "county": "España",
      "country_code": "ES", "postcode": "28001"
    },
    "store_details": {
      "customer_date_joined": "2022-03-17",
      "customer_last_login": "2022-03-17"
    },
    "financial": {
      "salary": 0, "currency": "EUR",
      "employment_status": "N/A", "contract_type": "N/A"
    },
    "other_data": [
      { "name": "Tienda", "type": "STRING", "value": "MI TIENDA" }
    ]
  },
  "order": {
    "id": "PEDIDO-2026-000123",
    "products": [
      {
        "id": "SKU-001", "name": "Producto", "quantity": "1",
        "price": "600.00", "tax_rate": 21,
        "description": "Descripción del producto",
        "url": "https://tu-tienda.com/producto",
        "image_url": "https://tu-tienda.com/producto.jpg"
      }
    ]
  },
  "flow_config": {
    "success_url": "https://tu-tienda.com/checkout/ok?invoice=PEDIDO-2026-000123",
    "notification_url": "https://tu-tienda.com/api/frakmenta/notifications",
    "ko_url": "https://tu-tienda.com/checkout/ko?invoice=PEDIDO-2026-000123"
  },
  "other_data": [ { "name": "N/A", "type": "STRING", "value": "N/A" } ],
  "signature": "<sha256 del punto 3>"
}
```

Notas de campos:

- `invoice_id`: identificador **único e inmutable** del pedido. Genéralo en el
  backend en cada intento; no lo aceptes desde el navegador.
- `product_price` (raíz) va en **céntimos**; `order.products[].price` va en
  **euros con dos decimales** (`"600.00"`).
- `flow_config.notification_url` debe ser una URL **pública HTTPS** de tu backend
  (Frakmenta la llama servidor-a-servidor). En local usa un túnel HTTPS.

### Respuesta y extracción del token

La API responde con un JSON que contiene el token. Según versión puede venir
como `token`, `data.token_url`, `transaction_token`, etc. **Busca de forma
tolerante** la primera de estas claves con valor string:

```text
token, transaction_token, operation_token, token_url  (incluye variantes en data.*)
```

Si `response.ok` es falso o no hay token, devuelve un error controlado a tu
frontend; no continúes al checkout.

---

## 5. Abrir el checkout (frontend)

Con el `token` que te devuelve TU backend, envía un formulario **POST nativo** a
Frakmenta, dirigido a un `iframe`:

`POST {BASE}/op/ecommerce/load`
Campos: `token=<token>` y `fk-lang=es`

```html
<iframe class="iframe-fk" id="frakmentaEcommerce" name="frameEcommerce" scrolling="no"></iframe>
<script>
  function abrirCheckoutFrakmenta(token, base) {
    const form = document.createElement('form');
    form.method = 'POST';
    form.action = base + '/op/ecommerce/load';
    form.target = 'frameEcommerce'; // = name del iframe oficial
    form.acceptCharset = 'UTF-8';
    form.hidden = true;
    for (const [name, value] of Object.entries({ token, 'fk-lang': 'es' })) {
      const input = document.createElement('input');
      input.type = 'hidden'; input.name = name; input.value = String(value);
      form.appendChild(input);
    }
    document.body.appendChild(form);
    // Envío nativo: evita que listeners del widget alteren el token.
    HTMLFormElement.prototype.submit.call(form);
    setTimeout(() => form.remove(), 1000);
  }
</script>
```

En DevTools debe verse:

```text
POST /api/frakmenta/operations (tu backend)     200
POST {BASE}/op/ecommerce/load                   200/302
  token:  <token real>
  fk-lang: es
```

No debe aparecer un token vacío, `[object Object]`, dos campos `fk-lang`, ni una
creación por `GET`.

---

## 6. El simulador de cuotas (frontend)

En el `<head>` carga los estilos y el script oficial (la **clave pública** sí
puede estar en el navegador):

```html
<link rel="stylesheet" href="{BASE}/lib/bootstrap/css/bootstrap.css">
<link rel="stylesheet" href="{BASE}/css/widget-ecommerce.css">
<link rel="stylesheet" href="{BASE}/css/ecommerce/style.css">
<script defer
  src="{BASE}/js/widgetEcommerce.js"
  data-name="widgetFK"
  data-api-url="{BASE}"
  data-apikey="TU_CLAVE_PUBLICA"></script>
```

Coloca el contenedor y ejecuta `simulator()` cuando el script esté cargado. El
`data-product_price` va en **céntimos**:

```html
<div id="fk-widget-installments" data-product_price="60000"></div>
<script>
  (function esperar(n){
    if (typeof window.simulator === 'function') return window.simulator();
    if (n < 30) setTimeout(() => esperar(n + 1), 200);
  })(0);
</script>
```

---

## 7. Confirmación del pedido (server-to-server)

**No confirmes el pedido solo por la `success_url` del navegador.** Confirma
con:

1. la **notificación server-to-server** que Frakmenta envía a
   `flow_config.notification_url` (responde `200`), o
2. una **consulta de estado** desde tu backend antes de dar el pedido por bueno.

Las devoluciones y reembolsos se gestionan desde el **Área Comercio**.

---

## 8. Checklist de integración (para el agente)

- [ ] Credenciales en variables de entorno del backend; clave privada fuera del
      frontend y del control de versiones.
- [ ] `BASE` = `beta2.frakmenta.com` durante el desarrollo.
- [ ] Firma SHA-256 validada contra el vector de prueba del punto 3.
- [ ] `product_price` en céntimos (raíz) y en euros con 2 decimales (líneas).
- [ ] `invoice_id` único generado en el backend en cada intento.
- [ ] Endpoint de creación `POST {BASE}/api/fk/v2/operations` funcionando (200).
- [ ] Extracción tolerante del `token`.
- [ ] Checkout con `POST` nativo a `{BASE}/op/ecommerce/load` (token + fk-lang).
- [ ] `notification_url` pública HTTPS; confirmación por webhook o consulta.
- [ ] Compra y devolución de prueba con la tarjeta de test superadas.
- [ ] Cambio a `frakmenta.com` y credenciales de producción como último paso.

---

## 9. Referencias

- Portal de desarrolladores: `https://www.findirect.es/frakmenta/desarrolladores/`
- Manual técnico API v2 (PDF): `https://www.findirect.es//wp-content/uploads/2021/11/FK-API-Frakmenta_2.00.pdf`
- Guía Web Checkout B2B (PPTX): `https://www.findirect.es//wp-content/uploads/2026/07/TEI-Guia-de-Integracion-Ecommerce-Web-Checkout-B2B-claro.pptx`
- Los ejemplos por lenguaje (PHP, Node.js, Python, cURL, C#, Java) están en la
  pestaña **Web Checkout** del portal de desarrolladores.

Reglas obligatorias:
- La clave privada y la firma SHA-256 solo en el backend; nunca en el frontend
  ni en el control de versiones.
- Firma = sha256(merchant_id|delegation|"e-commerce"|invoice_id|product_price|"EUR"|private_key),
  con product_price en céntimos.
- Crear operación: POST {BASE}/api/fk/v2/operations. Checkout: POST del token a
  {BASE}/op/ecommerce/load sobre un iframe, con token y fk-lang.
- Usa el entorno de pruebas https://beta2.frakmenta.com hasta validar con la
  tarjeta de test 4548 8144 7972 7229.
- Confirma el pedido por notificación server-to-server o consulta de estado, no
  solo por la success_url del navegador.

Detente y pídeme estas credenciales (Área Comercio): merchant_id, delegation,
clave pública y clave privada. No las inventes.

Al terminar, muéstrame: el endpoint de backend que crea la operación, el punto
del frontend que abre el iframe, y cómo has guardado las credenciales.

La seguridad de los mejores bancos,
sin ser un banco.