Planes (Prices)
¿Qué es un plan?
Sección titulada «¿Qué es un plan?»Un plan es la plantilla que usará cada suscripción para determinar cuánto y
cada cuánto se cobra: precio, moneda, frecuencia de la recurrencia (invoice_period
invoice_interval), días de prueba (trial_*) y período de gracia (grace_*). Un plan puede pertenecer a un grupo.
Checkout de suscripción alojado
Sección titulada «Checkout de suscripción alojado»No necesitas construir un formulario de suscripción. Cada plan trae una página de checkout alojada por Efipay, lista para usar. Al crear el plan la respuesta te devuelve:
| Campo | Qué es |
|---|---|
| checkoutUrl | La URL de la página donde tu cliente se suscribe: ingresa sus datos, su tarjeta y queda suscrito |
| qrCodeSvg | Esa misma URL como código QR, ya renderizado en SVG. Sirve para imprimirlo o mostrarlo en pantalla |
Es la ruta de integración más corta: creas el plan una vez, publicas el enlace y no escribes una sola línea de código de cobro. Efipay se encarga del formulario, la tokenización de la tarjeta y el cobro recurrente.
Desde ese mismo enlace tu suscriptor tiene un portal de autogestión donde puede consultar sus cobros y facturas, actualizar la tarjeta con la que paga, renovar y cancelar su suscripción. Se identifica con un código de verificación que le llega por correo, así que no tienes que darle usuario ni contraseña.
Crear plan
Sección titulada «Crear plan»Descripción: Crea un plan que servirá como guía para las suscripciones recurrentes (precio, moneda, frecuencia, prueba y gracia).
POST /api/v1/subscriptions/plan
| Nombre del campo | Descripción | Reglas |
|---|---|---|
| name | Nombre del plan. Lo ve el suscriptor | ['required', 'string', 'max:150'] |
| description | Qué incluye el plan | ['required', 'string', 'max:500'] |
| price | Valor que se cobra en cada renovación. El máximo depende de la moneda: 999999999999 en COP y 200000000 en USD/EUR |
['required', 'numeric', 'min:1', 'max:999999999999'] |
| currency_type | Moneda del cobro. Ver enumeraciones | ['required', 'in:COP,USD,EUR'] |
| tax | Valor del IVA a aplicar. Debe ser uno de tus impuestos activos; consúltalos en GET /api/v1/resources/get-taxes |
['nullable', 'exists:taxes,value'] |
| invoice_period | Cada cuántos invoice_interval se cobra. Junto con el intervalo define la frecuencia |
['required', 'integer', 'min:1', 'max:30'] |
| invoice_interval | Unidad de la frecuencia de cobro | ['required', 'in:day,week,month,year'] |
| trial_period | Períodos de prueba antes del primer cobro. 0 = sin prueba |
['sometimes', 'integer', 'min:0', 'max:30'] |
| trial_interval | Unidad del período de prueba | ['sometimes', 'in:day,week,month,year'] |
| grace_period | Tiempo tras el vencimiento en el que se sigue reintentando el cobro antes de dar la suscripción por inactiva | ['sometimes', 'integer', 'min:0', 'max:7'] |
| grace_interval | Unidad del período de gracia | ['sometimes', 'in:day,week,month,year'] |
| max_recurrences | Número máximo de cobros aprobados. Al alcanzarlo la suscripción se cancela sola | ['nullable', 'integer', 'min:1', 'max:100'] |
| deadline | Fecha tope de la suscripción. Al pasarla se cancela sola | ['nullable', 'date', 'date_format:Y-m-d H:i:s'] |
| active_subscribers_limit | Máximo de suscriptores activos que admite el plan | ['nullable', 'integer', 'max:100000'] |
| allow_multiple_subscriptions | Permite que un mismo suscriptor tenga más de una suscripción a este plan | ['sometimes', 'boolean'] |
| sort_order | Orden en que se listan los planes | ['nullable', 'integer', 'max:100000'] |
| subscription_group_id | Grupo al que pertenece el plan | ['nullable', 'exists:subscription_groups,id'] |
| office | Sucursal a la que pertenece el plan. Debe ser una de tus sucursales | ['required', 'exists:offices,id'] |
| advanced_options | Opciones avanzadas del cobro | ['nullable', 'array'] |
| advanced_options.result_urls | URLs de retorno y webhook para los cobros del plan | ['nullable', 'array'] |
| metadata | Tus pares clave-valor. Ver Metadata | ['sometimes', 'nullable', 'metadata'] |
Descuento del plan — se mantiene por compatibilidad. Para descuentos nuevos usa Cupones, que son reutilizables entre planes.
| Nombre del campo | Descripción | Reglas |
|---|---|---|
| discount_type_amount | Tipo de descuento. Ver enumeraciones | ['sometimes', 'nullable', 'in:value,percentage'] |
| discount_amount | Valor del descuento, según el tipo | ['sometimes', 'nullable', 'numeric'] |
| discount_period | Duración del descuento, en discount_interval |
['sometimes', 'integer', 'min:0', 'max:30'] |
| discount_interval | Unidad de la duración del descuento | ['sometimes', 'in:day,week,month,year'] |
| discount_subscribers_limit | Cuántos suscriptores alcanzan el descuento | ['sometimes', 'nullable', 'integer', 'min:1', 'max:100000'] |
curl -X POST \'/api/v1/subscriptions/plan' \-H 'Authorization: Bearer ACCESS_TOKEN' \-H 'Content-type: application/json' \-H 'Idempotency-Key: 9b1f0e34-6b2a-4a7e-9d0a-000000000002' \-d '{ "name": "Newsletter mensual", "description": "Suscripción general a newsletters, 15 envíos por mes", "price": 15000, "currency_type": "COP", "tax": 19, "invoice_period": 1, "invoice_interval": "month", "grace_period": 3, "grace_interval": "day", "office": 1}'{ "saved": true, "plan": { "id": "9b0f2a13-2c44-4a1a-9f2e-1d3b4c5d6e7f", "name": "Newsletter mensual", "description": "Suscripción general a newsletters, 15 envíos por mes", "price": 15000, "currencyType": "COP", "production": false, "trialInterval": "day", "trialPeriod": 0, "invoiceInterval": "month", "invoicePeriod": 1, "graceInterval": "day", "gracePeriod": 3, "discountInterval": "day", "discountPeriod": 0, "discountTypeAmount": null, "discountSubscribersLimit": null, "activeSubscribersLimit": null, "allowMultipleSubscriptions": false, "subscriptionGroupId": null, "groupName": null, "maxRecurrences": null, "deadline": null, "resultUrls": null, "createdAt": "2026-07-31 10:15:00", "active": true, "checkoutUrl": "https://sag.efipay.co/checkout/subscription-gateway/9b0f2a13-2c44-4a1a-9f2e-1d3b4c5d6e7f", "qrCodeSvg": "<svg xmlns=\"http://www.w3.org/2000/svg\" ...></svg>" }}{ "message": "El tax seleccionado no es válido.", "errors": { "tax": ["El tax seleccionado no es válido."], "office": ["El office seleccionado no es válido."] }}Actualizar plan
Sección titulada «Actualizar plan»Descripción: Actualiza los campos de un plan existente. Envía solo lo que quieras cambiar.
PUT /api/v1/subscriptions/plan/{plan-id}
| Nombre del campo | Descripción | Reglas |
|---|---|---|
| name | Nuevo nombre | ['sometimes', 'required', 'string', 'max:150'] |
| description | Nueva descripción | ['sometimes', 'required', 'string', 'max:500'] |
| price | Nuevo precio | ['sometimes', 'numeric', 'min:1'] |
| currency_type | Nueva moneda | ['sometimes', 'in:COP,USD,EUR'] |
| tax | Nuevo IVA, entre tus impuestos activos | ['sometimes', 'nullable', 'exists:taxes,value'] |
| invoice_period | Cada cuántos invoice_interval se cobra |
['sometimes', 'integer', 'min:1', 'max:30'] |
| invoice_interval | Unidad de la frecuencia de cobro | ['sometimes', 'in:day,week,month,year'] |
| trial_period | Períodos de prueba | ['sometimes', 'integer', 'min:0', 'max:30'] |
| trial_interval | Unidad del período de prueba | ['sometimes', 'in:day,week,month,year'] |
| grace_period | Períodos de gracia | ['sometimes', 'integer', 'min:0', 'max:7'] |
| grace_interval | Unidad del período de gracia | ['sometimes', 'in:day,week,month,year'] |
| discount_type_amount | Tipo de descuento | ['sometimes', 'nullable', 'in:value,percentage'] |
| discount_amount | Valor del descuento | ['sometimes', 'nullable', 'numeric'] |
| discount_period | Duración del descuento. Aquí el máximo es 100000, no 30 | ['sometimes', 'integer', 'min:0', 'max:100000'] |
| discount_interval | Unidad de la duración del descuento | ['sometimes', 'in:day,week,month,year'] |
| discount_subscribers_limit | Cuántos suscriptores alcanzan el descuento. Aquí no hay min:1 |
['sometimes', 'nullable', 'integer', 'max:100000'] |
| sort_order | Orden en que se listan los planes | ['sometimes', 'nullable', 'integer', 'max:100000'] |
| active_subscribers_limit | Máximo de suscriptores activos | ['sometimes', 'nullable', 'integer', 'max:100000'] |
| max_recurrences | Máximo de cobros aprobados | ['sometimes', 'nullable', 'integer', 'min:1', 'max:100'] |
| deadline | Fecha tope de la suscripción | ['sometimes', 'nullable', 'date_format:Y-m-d H:i:s'] |
| allow_multiple_subscriptions | Permitir varias suscripciones del mismo suscriptor | ['sometimes', 'boolean'] |
| subscription_group_id | Grupo al que pertenece el plan | ['sometimes', 'nullable', 'exists:subscription_groups,id'] |
| advanced_options | Opciones avanzadas. Envía null para borrarlas |
['nullable', 'array'] |
| advanced_options.result_urls | URLs de retorno y webhook | ['nullable', 'array'] |
| metadata | Se combina con la existente. Una clave con "" o null se borra; metadata: null borra todo. Ver Metadata |
['sometimes', 'nullable', 'metadata'] |
curl -X PUT \'/api/v1/subscriptions/plan/9b0f2a13-2c44-4a1a-9f2e-1d3b4c5d6e7f' \-H 'Authorization: Bearer ACCESS_TOKEN' \-H 'Content-type: application/json' \-d '{ "price": 30000, "description": "Suscripción básica a newsletter, 40 envíos por mes"}'{ "saved": true, "plan": { "id": "9b0f2a13-2c44-4a1a-9f2e-1d3b4c5d6e7f", "name": "Newsletter mensual", "description": "Suscripción básica a newsletter, 40 envíos por mes", "price": 30000, "currencyType": "COP", "production": false, "trialInterval": "day", "trialPeriod": 0, "invoiceInterval": "month", "invoicePeriod": 1, "graceInterval": "day", "gracePeriod": 3, "discountInterval": "day", "discountPeriod": 0, "discountTypeAmount": null, "discountSubscribersLimit": null, "activeSubscribersLimit": null, "allowMultipleSubscriptions": false, "subscriptionGroupId": null, "groupName": null, "maxRecurrences": null, "deadline": null, "resultUrls": null, "createdAt": "2026-07-31 10:15:00", "active": true, "checkoutUrl": "https://sag.efipay.co/checkout/subscription-gateway/9b0f2a13-2c44-4a1a-9f2e-1d3b4c5d6e7f", "qrCodeSvg": "<svg xmlns=\"http://www.w3.org/2000/svg\" ...></svg>" }}Metadata
Sección titulada «Metadata»metadata guarda tus propias referencias del plan (SKU, nivel, id en tu catálogo…), al
estilo de Stripe. Mismas reglas que en suscripciones:
| Límite | Valor |
|---|---|
| Claves | Hasta 50, de 1 a 40 caracteres [A-Za-z0-9_-] |
| Valor | Texto o número, hasta 500 caracteres. Se guarda como texto |
| Al actualizar | Las claves se combinan; "" o null borra una clave; metadata: null borra todo |
Llega en los GET y listados y en los
webhooks de suscripción, dentro de
subscription.plan.metadata. Filtra los listados con filter[metadata][clave]=valor.
Un valor inválido responde 422 con error.code: "validation_failed" y
error.param: "metadata".
Listar planes
Sección titulada «Listar planes»Descripción: Devuelve tus planes, paginados.
GET /api/v1/subscriptions/plan
Estos parámetros de consulta no se validan en el servidor: un valor inesperado no
produce un 422, simplemente no filtra.
| Nombre del campo | Descripción |
|---|---|
| active | Envía exactamente la cadena true para traer solo los planes activos. Cualquier otro valor no filtra |
| fields | Columnas a devolver, separadas por coma. Útil para respuestas ligeras |
| per_page | Planes por página. Por defecto 2, así que casi siempre querrás enviarlo |
| filter[metadata][clave] | Coincidencia exacta con un par de tu metadata, por ejemplo filter[metadata][tier]=gold |
La respuesta son los modelos crudos en snake_case dentro del sobre de paginación de
Laravel, no el objeto camelCase que devuelven crear y actualizar.
curl -X GET \'/api/v1/subscriptions/plan?active=true&per_page=50' \-H 'Authorization: Bearer ACCESS_TOKEN' \-H 'Accept: application/json'{ "current_page": 1, "data": [ { "id": "9b0f2a13-2c44-4a1a-9f2e-1d3b4c5d6e7f", "name": "Newsletter mensual", "description": "Suscripción general a newsletters, 15 envíos por mes", "price": 15000, "currency_type": "COP", "advanced_option_id": null, "tax": 19, "trial_period": 0, "trial_interval": "day", "invoice_period": 1, "invoice_interval": "month", "grace_period": 3, "grace_interval": "day", "discount_period": 0, "discount_interval": "day", "discount_subscribers_limit": null, "discount_type_amount": null, "discount_amount": null, "sort_order": 0, "active_subscribers_limit": null, "max_recurrences": null, "deadline": null, "production": false, "active": true, "subscription_group_id": null, "user_id": 42, "office_id": 1, "commerce_id": 315, "allow_multiple_subscriptions": false, "metadata": { "tier": "gold" }, "created_at": "2026-07-31 10:15:00", "checkout_url": "https://sag.efipay.co/checkout/subscription-gateway/9b0f2a13-2c44-4a1a-9f2e-1d3b4c5d6e7f", "checkout_url_commerce_assign": "https://sag.efipay.co/checkout/subscription-gateway/9b0f2a13-2c44-4a1a-9f2e-1d3b4c5d6e7f?commerce_assign=true" } ], "first_page_url": "https://sag.efipay.co/api/v1/subscriptions/plan?page=1", "from": 1, "last_page": 1, "last_page_url": "https://sag.efipay.co/api/v1/subscriptions/plan?page=1", "next_page_url": null, "path": "https://sag.efipay.co/api/v1/subscriptions/plan", "per_page": 50, "prev_page_url": null, "to": 1, "total": 1}Obtener un plan
Sección titulada «Obtener un plan»Descripción: Devuelve un plan por su id, como modelo crudo en snake_case.
GET /api/v1/subscriptions/plan/{plan-id}
curl -X GET \'/api/v1/subscriptions/plan/9b0f2a13-2c44-4a1a-9f2e-1d3b4c5d6e7f' \-H 'Authorization: Bearer ACCESS_TOKEN' \-H 'Accept: application/json'{ "id": "9b0f2a13-2c44-4a1a-9f2e-1d3b4c5d6e7f", "name": "Newsletter mensual", "description": "Suscripción general a newsletters, 15 envíos por mes", "price": 15000, "currency_type": "COP", "tax": 19, "trial_period": 0, "trial_interval": "day", "invoice_period": 1, "invoice_interval": "month", "grace_period": 3, "grace_interval": "day", "production": false, "active": true, "subscription_group_id": null, "office_id": 1, "commerce_id": 315, "allow_multiple_subscriptions": false, "created_at": "2026-07-31 10:15:00", "checkout_url": "https://sag.efipay.co/checkout/subscription-gateway/9b0f2a13-2c44-4a1a-9f2e-1d3b4c5d6e7f", "checkout_url_commerce_assign": "https://sag.efipay.co/checkout/subscription-gateway/9b0f2a13-2c44-4a1a-9f2e-1d3b4c5d6e7f?commerce_assign=true"}Planes de un grupo
Sección titulada «Planes de un grupo»Descripción: A partir del ID de un grupo, devuelve los planes de ese grupo.
GET /api/v1/subscriptions/plan/group/{group}/{all?}
Los dos segmentos son opcionales y cambian el resultado:
| Ruta | Devuelve |
|---|---|
/plan/group/{group} |
Solo los planes activos del grupo |
/plan/group/{group}/all |
Todos los planes del grupo, activos e inactivos |
/plan/group |
Los planes activos del comercio, sin filtrar por grupo |
Acepta también filter[metadata][clave]=valor para quedarte con los planes cuya
metadata coincide exactamente.
curl -X GET \'/api/v1/subscriptions/plan/group/9ae9a2e0-bf16-43c7-a052-f448c447c33e/all' \-H 'Authorization: Bearer ACCESS_TOKEN' \-H 'Accept: application/json'[ { "id": "9b0f2a13-2c44-4a1a-9f2e-1d3b4c5d6e7f", "name": "Newsletter mensual", "price": 15000, "currency_type": "COP", "invoice_period": 1, "invoice_interval": "month", "production": false, "active": true, "subscription_group_id": "9ae9a2e0-bf16-43c7-a052-f448c447c33e", "created_at": "2026-07-31 10:15:00" }]Activar / desactivar plan
Sección titulada «Activar / desactivar plan»Descripción: Activa o desactiva un plan. Un plan inactivo no puede usarse para crear nuevas suscripciones, pero las suscripciones existentes siguen cobrándose.
PUT /api/v1/subscriptions/plan/changeActive/{plan-id}
| Nombre del campo | Descripción | Reglas |
|---|---|---|
| active | Nuevo estado del plan | ['required', 'boolean'] |
curl -X PUT \'/api/v1/subscriptions/plan/changeActive/9b0f2a13-2c44-4a1a-9f2e-1d3b4c5d6e7f' \-H 'Authorization: Bearer ACCESS_TOKEN' \-H 'Content-type: application/json' \-d '{ "active": false }'{ "active": false, "plan": { "id": "9b0f2a13-2c44-4a1a-9f2e-1d3b4c5d6e7f", "name": "Newsletter mensual", "price": 15000, "currencyType": "COP", "active": false, "createdAt": "2026-07-31 10:15:00", "checkoutUrl": "https://sag.efipay.co/checkout/subscription-gateway/9b0f2a13-2c44-4a1a-9f2e-1d3b4c5d6e7f", "qrCodeSvg": "<svg xmlns=\"http://www.w3.org/2000/svg\" ...></svg>" }}Eliminar plan
Sección titulada «Eliminar plan»Descripción: Elimina un plan por su id.
DELETE /api/v1/subscriptions/plan/{plan-id}
curl -X DELETE \'/api/v1/subscriptions/plan/9b0f2a13-2c44-4a1a-9f2e-1d3b4c5d6e7f' \-H 'Authorization: Bearer ACCESS_TOKEN' \-H 'Accept: application/json'{ "deleted": true}{ "message": "El plan tiene suscripciones activas y no puede eliminarse."}Alias price
Sección titulada «Alias price»Las mismas operaciones, con el nombre de Stripe. Comportamiento, parámetros y respuestas
son idénticos a los de arriba. Fíjate en que el filtro por grupo usa product en lugar
de group.
Listar — GET /api/v1/subscriptions/price (recuerda per_page, que por defecto es 2)
Obtener uno — GET /api/v1/subscriptions/price/{price-id}
Por grupo — GET /api/v1/subscriptions/price/product/{product-id}/{all?}
Crear — POST /api/v1/subscriptions/price
Actualizar — PUT /api/v1/subscriptions/price/{price-id}
Activar / desactivar — PUT /api/v1/subscriptions/price/changeActive/{price-id}
Eliminar — DELETE /api/v1/subscriptions/price/{price-id}