> ## Documentation Index
> Fetch the complete documentation index at: https://docs.v2.topup.com.co/llms.txt
> Use this file to discover all available pages before exploring further.

# PayIn

> Country-specific information for PayIn transactions in Peru.

## Overview

The PayIn API for Peru enables customer deposit transactions through multiple payment methods including bank transfers, QR code payments, and PagoEfectivo cash vouchers. Transactions are processed in real-time with webhook notifications for status updates.

### Key Features

* **Multiple Payment Methods**: `BANK_TRANSFER`, `QR`, `PAGOEFECTIVO`
* **Currency**: Peruvian Sol (`PEN`)
* **Real-time Processing**: Immediate transaction validation and processing
* **Webhook Notifications**: Asynchronous status updates via IPN
* **Secure Redirect Flow**: Customer completes payment on secure hosted page

### Quick Reference

| Parameter         | Value                | Description                |
| ----------------- | -------------------- | -------------------------- |
| **Endpoint**      | `POST /api/v1/payin` | Initiate PayIn transaction |
| **Currency**      | `PEN`                | Peruvian Sol               |
| **Country Code**  | `PE`                 | Peru                       |
| **Auth Required** | Yes                  | Token-Top + Basic Auth     |

***

## Request Parameters

### Required Fields

| Field            | Type   | Description                                    | Example                                     |
| ---------------- | ------ | ---------------------------------------------- | ------------------------------------------- |
| `reference`      | string | Unique transaction identifier from your system | `"13cNPNGbX7meiMppXzVz7g781ysektqq5X"`      |
| `amount`         | float  | Transaction amount in PEN                      | `1.50` (= S/ 1.50 PEN)                      |
| `currency`       | string | Three-letter currency code (ISO 4217)          | `"PEN"`                                     |
| `country`        | string | Two-letter country code (ISO 3166-1 alpha-2)   | `"PE"`                                      |
| `payment_method` | string | Payment method identifier                      | `"ALL_METHODS"`,`"CARD"`                    |
| `ipn_url`        | string | Webhook URL for transaction status updates     | `"https://your-domain.com/webhook"`         |
| `redirect_url`   | string | URL to redirect customer after payment         | `"https://your-domain.com/payment/success"` |
| `customer_data`  | object | Customer information object (see below)        | -                                           |

### Customer Data Object

| Field            | Type   | Required | Description                              | Example               |
| ---------------- | ------ | -------- | ---------------------------------------- | --------------------- |
| `legal_doc`      | string | ✅        | Customer's legal document number         | `"12345678"`          |
| `legal_doc_type` | string | ✅        | Document type: `DNI`, `RUC`, `CE`, `PPN` | `"DNI"`               |
| `phone_code`     | string | ✅        | Country calling code                     | `"51"`                |
| `phone_number`   | string | ✅        | Phone number without country code        | `"987654321"`         |
| `email`          | string | ✅        | Customer email address                   | `"johndoe@email.com"` |
| `full_name`      | string | ✅        | Customer full name                       | `"John Doe"`          |

### Optional Fields

| Field             | Type    | Description                        | Default          |
| ----------------- | ------- | ---------------------------------- | ---------------- |
| `description`     | string  | Transaction description            | -                |
| `expiration_time` | integer | Payment link expiration in minutes | `720` (12 hours) |

***

## Response Structure

### Success Response

| Field     | Type   | Description                      |
| --------- | ------ | -------------------------------- |
| `code`    | string | Response code (`"01"` = success) |
| `status`  | string | Transaction status (`"SUCCESS"`) |
| `message` | string | Description of the response      |
| `data`    | object | Transaction data object          |

### Data Object

| Field         | Type   | Description                                 |
| ------------- | ------ | ------------------------------------------- |
| `ticket`      | string | Unique transaction identifier (TumiPay ID)  |
| `date`        | string | Transaction timestamp (YYYY-MM-DD HH:MM:SS) |
| `payment_url` | string | Secure payment URL for customer redirect    |
| `transaction` | object | Transaction details echo                    |

***

## Examples

### Request Example

<CodeGroup dropdown>
  ```bash cURL theme={null}
  curl --request POST 'https://api-empresas.staging.topup.com.co/production/api/v1/payin' \
  --header 'Token-Top: your_auth_token' \
  --header 'Authorization: Basic your_auth_key' \
  --header 'Content-Type: application/json' \
  --data-raw '{
      "reference": "13cNPNGbX7meiMppXzVz7g781ysektqq5X",
      "amount": 150,
      "currency": "PEN",
      "country": "PE",
      "payment_method": "ALL_METHODS",
      "description": "Test PayIn",
      "customer_data": {
          "legal_doc": "12345678",
          "legal_doc_type": "DNI",
          "phone_code": "51",
          "phone_number": "987654321",
          "email": "johndoe@email.com",
          "full_name": "John Doe"
      },
      "expiration_time": 720,
      "ipn_url": "https://your-domain.com/webhook",
      "redirect_url": "https://your-domain.com/payment/success"
  }'
  ```

  ```rust Rust theme={null}
  use reqwest::Client;
  use serde_json::json;

  async fn make_payin_request() -> Result<(), reqwest::Error> {
      let client = Client::new();
      let response = client.post("https://api-empresas.staging.topup.com.co/production/api/v1/payin")
          .header("Token-Top", "your_auth_token")
          .header("Authorization", "Basic your_auth_key")
          .header("Content-Type", "application/json")
          .json(&json!({
              "reference": "13cNPNGbX7meiMppXzVz7g781ysektqq5X",
              "amount": 150,
              "currency": "PEN",
              "country": "PE",
              "payment_method": "ALL_METHODS",
              "description": "Test PayIn",
              "customer_data": {
                  "legal_doc": "12345678",
                  "legal_doc_type": "DNI",
                  "phone_code": "51",
                  "phone_number": "987654321",
                  "email": "johndoe@email.com",
                  "full_name": "John Doe"
              },
              "expiration_time": 720,
              "ipn_url": "https://your-domain.com/webhook",
              "redirect_url": "https://your-domain.com/payment/success"
          }))
          .send()
          .await?;

      let response_json: serde_json::Value = response.json().await?;
      println!("{:#?}", response_json);

      Ok(())
  }
  ```

  ```typescript TypeScript theme={null}
  import axios from 'axios';

  const makePayinRequest = async () => {
    try {
      const response = await axios.post('https://api-empresas.staging.topup.com.co/production/api/v1/payin', {
        reference: '13cNPNGbX7meiMppXzVz7g781ysektqq5X',
        amount: 150,
        currency: 'PEN',
        country: 'PE',
        payment_method: 'ALL_METHODS',
        description: 'Test PayIn',
        customer_data: {
          legal_doc: '12345678',
          legal_doc_type: 'DNI',
          phone_code: '51',
          phone_number: '987654321',
          email: 'johndoe@email.com',
          full_name: 'John Doe'
        },
        expiration_time: 720,
        ipn_url: 'https://your-domain.com/webhook',
        redirect_url: 'https://your-domain.com/payment/success'
      }, {
        headers: {
          'Token-Top': 'your_auth_token',
          'Authorization': 'Basic your_auth_key',
          'Content-Type': 'application/json'
        }
      });
      console.log(response.data);
    } catch (error) {
      console.error(error);
    }
  };

  makePayinRequest();
  ```

  ```php PHP theme={null}
  <?php
  $curl = curl_init();

  curl_setopt_array($curl, array(
    CURLOPT_URL => 'https://api-empresas.staging.topup.com.co/production/api/v1/payin',
    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 =>'{
      "reference": "13cNPNGbX7meiMppXzVz7g781ysektqq5X",
      "amount": 150,
      "currency": "PEN",
      "country": "PE",
      "payment_method": "ALL_METHODS",
      "description": "Test PayIn",
      "customer_data": {
          "legal_doc": "12345678",
          "legal_doc_type": "DNI",
          "phone_code": "51",
          "phone_number": "987654321",
          "email": "johndoe@email.com",
          "full_name": "John Doe"
      },
      "expiration_time": 720,
      "ipn_url": "https://your-domain.com/webhook",
      "redirect_url": "https://your-domain.com/payment/success"
  }',
    CURLOPT_HTTPHEADER => array(
      'Token-Top: your_auth_token',
      'Authorization: Basic your_auth_key',
      'Content-Type: application/json'
    ),
  ));

  $response = curl_exec($curl);

  curl_close($curl);
  echo $response;
  ```

  ```java Java theme={null}
  import java.net.http.HttpClient;
  import java.net.http.HttpRequest;
  import java.net.http.HttpResponse;
  import java.net.URI;
  import java.io.IOException;

  public class PayinRequest {
      public static void main(String[] args) throws IOException, InterruptedException {
          HttpClient client = HttpClient.newHttpClient();
          String json = "{ \\"reference\\": \\"13cNPNGbX7meiMppXzVz7g781ysektqq5X\\", \\"amount\\": 150, \\"currency\\": \\"PEN\\", \\"country\\": \\"PE\\", \\"payment_method\\": \\"ALL_METHODS\\", \\"description\\": \\"Test PayIn\\", \\"customer_data\\": { \\"legal_doc\\": \\"12345678\\", \\"legal_doc_type\\": \\"DNI\\", \\"phone_code\\": \\"51\\", \\"phone_number\\": \\"987654321\\", \\"email\\": \\"johndoe@email.com\\", \\"full_name\\": \\"John Doe\\" }, \\"expiration_time\\": 720, \\"ipn_url\\": \\"https://your-domain.com/webhook\\", \\"redirect_url\\": \\"https://your-domain.com/payment/success\\" }";

          HttpRequest request = HttpRequest.newBuilder()
                  .uri(URI.create("https://api-empresas.staging.topup.com.co/production/api/v1/payin"))
                  .header("Token-Top", "your_auth_token")
                  .header("Authorization", "Basic your_auth_key")
                  .header("Content-Type", "application/json")
                  .POST(HttpRequest.BodyPublishers.ofString(json))
                  .build();

          HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

          System.out.println(response.body());
      }
  }
  ```

  ```go Go theme={null}
  package main

  import (
      "fmt"
      "net/http"
      "io/ioutil"
      "strings"
  )

  func main() {
      url := "https://api-empresas.staging.topup.com.co/production/api/v1/payin"
      method := "POST"

      payload := strings.NewReader(`{
          "reference": "13cNPNGbX7meiMppXzVz7g781ysektqq5X",
          "amount": 150,
          "currency": "PEN",
          "country": "PE",
          "payment_method": "ALL_METHODS",
          "description": "Test PayIn",
          "customer_data": {
              "legal_doc": "12345678",
              "legal_doc_type": "DNI",
              "phone_code": "51",
              "phone_number": "987654321",
              "email": "johndoe@email.com",
              "full_name": "John Doe"
          },
          "expiration_time": 720,
          "ipn_url": "https://your-domain.com/webhook",
          "redirect_url": "https://your-domain.com/payment/success"
      }`)

      client := &http.Client {}
      req, err := http.NewRequest(method, url, payload)

      if err != nil {
          fmt.Println(err)
          return
      }
      req.Header.Add("Token-Top", "your_auth_token")
      req.Header.Add("Authorization", "Basic your_auth_key")
      req.Header.Add("Content-Type", "application/json")

      res, err := client.Do(req)
      if err != nil {
          fmt.Println(err)
          return
      }
      defer res.Body.Close()

      body, err := ioutil.ReadAll(res.Body)
      if err != nil {
          fmt.Println(err)
          return
      }
      fmt.Println(string(body))
  }
  ```
</CodeGroup>

### Response Example

```json theme={null}
{
    "code": "01",
    "status": "SUCCESS",
    "message": "Operacion exitosa",
    "data": {
        "ticket": "8BNsCFva1NKPqy2",
        "date": "2025-10-15 17:58:36",
        "payment_url": "https://link.staging.topup.com.co/payments/main?s=8BNsCFva1NKPqy2",
        "transaction": {
            "reference": "13cNPNGbX7meiMppXzVz7g781ysektqq5X",
            "amount": 5000,
            "currency": "PEN",
            "payment_method": "ALL_METHODS",
            "redirect_url": "https://your-domain.com/payment/success",
            "ipn_url": "https://your-domain.com/webhook",
            "description": "Test PayIn"
        }
    }
}
```

***

## QR Code Direct Flow

<Note>
  El flujo QR directo está disponible **exclusivamente para Perú** (`country: "PE"`) con `payment_method: "QR"`. Requiere que el comercio tenga habilitado el parámetro `ExperienciaQrCode` en la configuración de TumiPay. Contacta al equipo de integraciones para activarlo.
</Note>

A diferencia del flujo tradicional (que devuelve un `payment_url` para redirigir al cliente a una página de pago), el flujo QR directo genera el código QR **en el momento de crear la transacción** y lo devuelve en la respuesta como una imagen en base64. El comercio es responsable de renderizarlo y mostrárselo al cliente.

### ¿Cómo funciona?

```
Merchant                   TumiPay API              StreamPayments
   |                            |                         |
   |-- POST /api/v1/payin ----→ |                         |
   |   payment_method: "QR"     |                         |
   |   country: "PE"            |-- Create QR Order ----→ |
   |                            |                         |
   |                            |←-- qrCode (string) ---- |
   |                            |                         |
   |                            | [normalize to base64]   |
   |                            |                         |
   |←-- 200 OK (qr_code_base64) |                         |
   |                            |                         |
   | [display QR to customer]   |                         |
   |                            |                         |
   |              Customer scans QR with bank app         |
   |                            |                         |
   |                            |←-- Payment confirmed -- |
   |                            |                         |
   |←-- Webhook (ipn_url) ----- |                         |
```

### Límites de monto para QR

| Límite                              | Valor por defecto | Descripción                                                                             |
| ----------------------------------- | ----------------- | --------------------------------------------------------------------------------------- |
| Monto máximo por transacción (Perú) | S/ 500 PEN        | Límite del proveedor QR para Perú. Se devuelve `QR_AMOUNT_LIMIT_EXCEEDED` si se supera. |

<Warning>
  Si el monto supera el límite máximo configurado para QR en Perú, el sistema devuelve **HTTP 422** con `error: "QR_AMOUNT_LIMIT_EXCEEDED"` antes de crear la transacción.
</Warning>

### Request (QR directo)

El request es idéntico al flujo estándar. El único cambio es `payment_method: "QR"`:

<CodeGroup dropdown>
  ```bash cURL theme={null}
  curl --request POST 'https://api-empresas.staging.topup.com.co/production/api/v1/payin' \
  --header 'Token-Top: your_auth_token' \
  --header 'Authorization: Basic your_auth_key' \
  --header 'Content-Type: application/json' \
  --data-raw '{
      "reference": "unique-ref-qr-001",
      "amount": 50,
      "currency": "PEN",
      "country": "PE",
      "payment_method": "QR",
      "description": "Pago QR directo",
      "customer_data": {
          "legal_doc": "12345678",
          "legal_doc_type": "DNI",
          "phone_code": "51",
          "phone_number": "987654321",
          "email": "johndoe@email.com",
          "full_name": "John Doe"
      },
      "expiration_time": 30,
      "ipn_url": "https://your-domain.com/webhook",
      "redirect_url": "https://your-domain.com/payment/success"
  }'
  ```

  ```typescript TypeScript theme={null}
  import axios from 'axios';

  const makeQrPayinRequest = async () => {
    try {
      const response = await axios.post('https://api-empresas.staging.topup.com.co/production/api/v1/payin', {
        reference: 'unique-ref-qr-001',
        amount: 50,
        currency: 'PEN',
        country: 'PE',
        payment_method: 'QR',
        description: 'Pago QR directo',
        customer_data: {
          legal_doc: '12345678',
          legal_doc_type: 'DNI',
          phone_code: '51',
          phone_number: '987654321',
          email: 'johndoe@email.com',
          full_name: 'John Doe'
        },
        expiration_time: 30,
        ipn_url: 'https://your-domain.com/webhook',
        redirect_url: 'https://your-domain.com/payment/success'
      }, {
        headers: {
          'Token-Top': 'your_auth_token',
          'Authorization': 'Basic your_auth_key',
          'Content-Type': 'application/json'
        }
      });

      const { qr_code_base_64 } = response.data.data;
      // Renderiza el QR en un elemento <img>:
      // <img src={`data:image/png;base64,${qr_code_base_64}`} />
      console.log(response.data);
    } catch (error) {
      console.error(error);
    }
  };

  makeQrPayinRequest();
  ```

  ```php PHP theme={null}
  <?php
  $curl = curl_init();

  curl_setopt_array($curl, array(
    CURLOPT_URL => 'https://api-empresas.staging.topup.com.co/production/api/v1/payin',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_POSTFIELDS => '{
      "reference": "unique-ref-qr-001",
      "amount": 50,
      "currency": "PEN",
      "country": "PE",
      "payment_method": "QR",
      "description": "Pago QR directo",
      "customer_data": {
          "legal_doc": "12345678",
          "legal_doc_type": "DNI",
          "phone_code": "51",
          "phone_number": "987654321",
          "email": "johndoe@email.com",
          "full_name": "John Doe"
      },
      "expiration_time": 30,
      "ipn_url": "https://your-domain.com/webhook",
      "redirect_url": "https://your-domain.com/payment/success"
  }',
    CURLOPT_HTTPHEADER => array(
      'Token-Top: your_auth_token',
      'Authorization: Basic your_auth_key',
      'Content-Type: application/json'
    ),
  ));

  $response = curl_exec($curl);
  curl_close($curl);

  $data = json_decode($response, true);
  $qrBase64 = $data['data']['qr_code_base_64'] ?? null;
  // Renderizar: '<img src="data:image/png;base64,' . $qrBase64 . '">'
  echo $response;
  ```

  ```python Python theme={null}
  import requests
  import json

  url = "https://api-empresas.staging.topup.com.co/production/api/v1/payin"

  headers = {
      'Token-Top': 'your_auth_token',
      'Authorization': 'Basic your_auth_key',
      'Content-Type': 'application/json'
  }

  payload = {
      "reference": "unique-ref-qr-001",
      "amount": 50,
      "currency": "PEN",
      "country": "PE",
      "payment_method": "QR",
      "description": "Pago QR directo",
      "customer_data": {
          "legal_doc": "12345678",
          "legal_doc_type": "DNI",
          "phone_code": "51",
          "phone_number": "987654321",
          "email": "johndoe@email.com",
          "full_name": "John Doe"
      },
      "expiration_time": 30,
      "ipn_url": "https://your-domain.com/webhook",
      "redirect_url": "https://your-domain.com/payment/success"
  }

  response = requests.post(url, headers=headers, data=json.dumps(payload))
  result = response.json()

  qr_base64 = result['data'].get('qr_code_base_64')
  # Renderizar con: f'<img src="data:image/png;base64,{qr_base64}">'
  ```
</CodeGroup>

### Response (QR directo)

La respuesta incluye todos los campos estándar más el campo `qr_code_base_64` dentro de `data`.

| Campo                  | Tipo   | Descripción                                                                                              |
| ---------------------- | ------ | -------------------------------------------------------------------------------------------------------- |
| `code`                 | string | `"01"` = éxito, `"00"` = error                                                                           |
| `status`               | string | `"SUCCESS"` o `"ERROR"`                                                                                  |
| `message`              | string | Descripción de la respuesta                                                                              |
| `data.ticket`          | string | Identificador único de la transacción (UUID de TumiPay)                                                  |
| `data.date`            | string | Fecha y hora de creación (`YYYY-MM-DD HH:MM:SS`)                                                         |
| `data.payment_url`     | string | URL de la página de pago (disponible como fallback)                                                      |
| `data.qr_code_base_64` | string | **Imagen QR en base64** (PNG). Muéstrala directamente en un `<img>` con prefijo `data:image/png;base64,` |
| `data.transaction`     | object | Echo de los datos de la transacción enviados en el request                                               |

```json theme={null}
{
    "code": "01",
    "status": "SUCCESS",
    "message": "Operacion exitosa",
    "data": {
        "ticket": "3f7a9b2c-1d4e-5f6a-7b8c-9d0e1f2a3b4c",
        "date": "2026-05-11 14:30:00",
        "payment_url": "https://link.staging.topup.com.co/payments/main?s=3f7a9b2c-1d4e-5f6a-7b8c-9d0e1f2a3b4c",
        "qr_code_base_64": "iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAYAAAB5fY51AAAA...",
        "transaction": {
            "reference": "unique-ref-qr-001",
            "amount": 50,
            "currency": "PEN",
            "payment_method": "QR",
            "redirect_url": "https://your-domain.com/payment/success",
            "ipn_url": "https://your-domain.com/webhook",
            "description": "Pago QR directo"
        }
    }
}
```

### Renderizar el QR

Con el valor de `qr_code_base_64` puedes mostrar directamente el QR al cliente sin necesidad de procesar la imagen:

```html theme={null}
<!-- HTML -->
<img src="data:image/png;base64,{{ qr_code_base_64 }}" alt="Escanea para pagar" />
```

```typescript theme={null}
// React / TypeScript
<img src={`data:image/png;base64,${data.qr_code_base_64}`} alt="Escanea para pagar" />
```

### Errores específicos del flujo QR

| HTTP | `code` | `error`                    | Descripción                                                |
| ---- | ------ | -------------------------- | ---------------------------------------------------------- |
| 422  | `"00"` | `QR_AMOUNT_LIMIT_EXCEEDED` | El monto supera el límite máximo permitido para QR en Perú |
| 400  | `"00"` | `TRANSACTION_IN_PROGRESS`  | Ya existe una transacción activa para ese email/monto      |
| 200  | `"00"` | —                          | Error al generar el QR con el proveedor (reintenta)        |

### Notificación por webhook

Al igual que el flujo estándar, cuando el cliente escanea y completa el pago, TumiPay envía una notificación a `ipn_url`. Consulta la [documentación de webhooks](/docs/webhooks) para ver la estructura del payload.
