Crear Oferta P2P
Crear una nueva oferta de compra o venta P2P.
POST /p2p/create
Crea una nueva oferta P2P de compra o venta. Para ofertas de venta (sell), el monto se deduce automáticamente del saldo del usuario. El usuario debe tener KYC verificado.
Autenticación
Bearer Token (Authorization: Bearer <token>) o Credenciales de App (app-id + app-secret en headers).
También puedes llamar a esta ruta con app-id / app-secret en vez de un token de cuenta. Ver Operar con credenciales de app.
Cuando se usa Credenciales de App, la oferta se crea en nombre del usuario propietario de la app. Dicho usuario debe cumplir todos los requisitos de P2P (p2p_enabled, KYC verificado, Telegram vinculado y teléfono verificado), y para ofertas de tipo sell el saldo se descuenta de su cuenta.
Request
Con Bearer Token:
curl -X POST "https://api.qvapay.com/p2p/create" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"type": "buy",
"coin": "BANK_CUP",
"amount": 50,
"receive": 12500,
"details": [{ "name": "cuenta", "value": "1234567890" }],
"only_vip": 0,
"private": 0,
"message": "Pago rápido",
"webhook": "https://example.com/webhook"
}'Con Credenciales de App:
curl -X POST "https://api.qvapay.com/p2p/create" \
-H "app-id: {tu-app-uuid}" \
-H "app-secret: {tu-app-secret}" \
-H "Content-Type: application/json" \
-d '{
"type": "buy",
"coin": "BANK_CUP",
"amount": 50,
"receive": 12500,
"details": [{ "name": "cuenta", "value": "1234567890" }],
"only_vip": 0,
"private": 0,
"message": "Pago rápido",
"webhook": "https://example.com/webhook"
}'Parámetros (body JSON)
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
type | string | Sí | Tipo de oferta: buy o sell |
coin | string | number | Sí | Tick de la moneda (ej: BANK_CUP) o ID numérico |
amount | number | Sí | Cantidad en QUSD (0.1 - 10,000) |
receive | number | Sí | Cantidad a recibir en la moneda seleccionada (0.1 - 100,000,000) |
details | array | Sí | Datos de pago según los campos de la moneda (ej: cuenta bancaria) |
only_kyc | number | No | 1 para restringir a usuarios con KYC verificado |
only_vip | number | No | 1 para restringir a usuarios VIP |
private | number | No | 1 para crear oferta privada (no se publica en Telegram) |
message | string | No | Mensaje público de la oferta (máx. 79 caracteres, URLs eliminadas) |
webhook | string | No | URL de webhook para notificaciones de estado |
tags | array | No | Etiquetas para la oferta (máx. 10 tags) |
offer_kind | string | No | fixed (por defecto, se toma entera) o flexible (por rango, se toma por partes). Ver Ofertas por rango |
order_min | number | Si offer_kind es flexible | Mínimo por orden (> 0) |
order_max | number | Si offer_kind es flexible | Máximo por orden (≥ order_min, ≤ amount) |
requirements | object | No | Requisitos para quien aplique (ver abajo). Valen para ofertas fixed y flexible |
idempotency_key | string | No | Clave de idempotencia (8-64 caracteres, [A-Za-z0-9._-]) para reintentos seguros en conexiones inestables. Repetir la misma clave en 24h devuelve la oferta original con 200 y duplicate: true en vez de crear un duplicado |
Requisitos para quien aplique
Con requirements solo podrán aplicar las cuentas que los cumplan:
{ "requirements": { "min_completed": 25, "min_rating": 4.5 } }| Clave | Valores permitidos | Qué exige |
|---|---|---|
min_completed | 5, 10, 25, 50, 100 | Operaciones P2P completadas |
min_rating | 4, 4.5 | Rating promedio (1–5) |
min_account_days | 7, 30, 90, 180 | Antigüedad de la cuenta en días |
min_trustscore | 50, 85 | TrustScore de la cuenta (0–100, lo asigna QvaPay) |
Cualquier otro valor responde 400 con code: "INVALID_REQUIREMENTS". Omitir una clave (o mandarla en 0/null) es "sin ese requisito".
La respuesta devuelve requirements (o null).
Response (201)
{
"msg": "Oferta P2P creada correctamente",
"p2p": {
"uuid": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"type": "buy",
"coin": "BANK_CUP",
"amount": 50.00,
"receive": 12500.00,
"only_kyc": 0,
"private": 0,
"only_vip": 0,
"status": "open",
"details": { "cuenta": "1234567890" },
"message": "Pago rápido",
"webhook": "https://example.com/webhook",
"created_at": "2024-06-20T14:30:00.000Z",
"updated_at": "2024-06-20T14:30:00.000Z"
}
}Webhook
Si se proporciona un webhook, se enviará un POST con el siguiente payload cuando la oferta sea creada:
{
"operation": "created",
"p2p": { ... }
}Idempotencia
Si se envía idempotency_key y una oferta ya fue creada con esa misma clave, la respuesta es 200 con la oferta original y el campo adicional "duplicate": true (no se crea una segunda oferta ni se debita saldo de nuevo). Si la creación original aún está en proceso, se responde 409 con "code": "DUPLICATE_REQUEST" — reintenta unos segundos después.
Errores
| Código | Descripción |
|---|---|
400 | Datos inválidos, moneda no encontrada, KYC requerido, saldo insuficiente, o límite de ofertas pendientes alcanzado |
401 | Token de autenticación inválido o ausente |
403 | FLEXIBLE_NOT_ENABLED: tu cuenta no tiene KYC aprobado y no puede publicar ofertas por rango |
409 | DUPLICATE_REQUEST: ya hay una creación en proceso con la misma idempotency_key |
409 | FLEXIBLE_OFFER_EXISTS: ya tienes una oferta por rango viva del mismo lado (buy/sell) en esa moneda; cancélala o edítala antes de publicar otra |
429 | Demasiadas solicitudes (límite: 1 cada 10 segundos; 100 ofertas al día (250 para cuentas VIP y GOLD), sin tope diario para cuentas de empresa) |
500 | Error interno al crear la transacción |