Saltar al contenido principal
Requiere PHP 8.2 o posterior y Laravel 12 o 13.

Instalación

stockshift:install publica config/stockshift.php, agrega STOCKSHIFT_TOKEN y STOCKSHIFT_WEBHOOK_SECRET a .env y .env.example y te pide sus valores. Crea una clave de API en StockShift, en Configuración > Acceso > Claves de API. Luego compruébala:

Inicio rápido

Hacer llamadas

El facade tiene todas las áreas de la API, como catalog() o inventory(), y cada área tiene un método por cada operación. Son los métodos del SDK, con los mismos argumentos y tipos de PHPStan:
También puedes llamar a una ruta directamente con get(), post(), put(), patch(), delete() o request():
El facade, un StockShift\Laravel\Connection inyectado y un StockShift\Sdk\StockShift inyectado usan todos la conexión predeterminada.

Consultas

Cada lista de la API tiene un constructor de consultas. where() envía los filtros propios de la API y los resultados llegan como colecciones de Laravel:
lazy() devuelve una LazyCollection que obtiene la página siguiente solo cuando llegas a ella. Con limit() o take(), las páginas más allá del límite nunca se obtienen:
La API devuelve cada lista en su propio orden fijo. orderBy() y orderByDesc() ordenan aquí en su lugar, así que leen primero la lista completa. Los campos pueden usar notación de puntos. Los constructores del facade son items(), stock(), movements(), counts(), transfers(), locations(), suppliers(), purchaseOrders(), customers(), salesOrders(), quotes(), invoices(), returns(), builds(), boms() y workCenters(). Cualquier otra lista funciona con su ruta:
Otros métodos: Los enums se envían como su valor y las fechas en ISO 8601.

Conexiones

Una conexión es un token, el secreto de webhook que le corresponde y su configuración. Agrega más en config/stockshift.php para llamar a varias organizaciones:
Para inyectar una, marca el parámetro:
Cada conexión tiene esta configuración:

Un token para cada tenant

En una app donde cada tenant conecta su propia organización de StockShift, busca el token antes de cada llamada:
O crea una conexión para un solo token donde la necesites:
Los resultados de consulta guardados en caché y los límites de solicitudes se mantienen separados para cada token.

Errores

Una llamada fallida lanza la ApiException del SDK, que contiene el cuerpo de detalles del problema de RFC 9457. Decide según errorCode, que es estable:
Una llamada que no recibe respuesta lanza una ConnectionException. Ambas extienden StockShiftException.

Reintentos, claves de idempotencia y llamadas en cola

El SDK reintenta un 429 después del tiempo que indica Retry-After. Reintenta un 5xx o una falla de red para GET, PUT y DELETE, y para un POST o PATCH solo cuando tiene una clave de idempotencia:
later() envía una llamada desde un job en cola. Un POST o PATCH recibe una clave de idempotencia cuando se crea el job, así que los reintentos del propio job envían la misma clave y la API hace el cambio una sola vez:
El job es StockShift\Laravel\Jobs\SendRequest. Lo intenta cinco veces. Una respuesta 4xx distinta de 429 lo hace fallar de inmediato, porque volver a enviarla da la misma respuesta. Un token de withToken() viaja con el job. El job se cifra en la cola.

Límites de solicitudes

Define rate_limit en una conexión para limitar las llamadas por minuto que tu app hace con un token. Todos los procesos que comparten el almacén de caché comparten el conteo. Una llamada que superaría el límite espera hasta que se pueda enviar. Déjalo en null para depender del límite de la API, que el cliente ya espera. Las solicitudes pasan por el cliente HTTP de Laravel, así que Http::fake(), los eventos RequestSending y ResponseReceived y herramientas como Telescope las ven.

Webhooks

El paquete agrega una ruta POST /stockshift/webhook. Dale su URL completa a StockShift cuando agregues un endpoint de webhook y pon en STOCKSHIFT_WEBHOOK_SECRET el secreto de firma que te muestra StockShift. Cada entrega se revisa contra el encabezado StockShift-Signature. Una que falla recibe un 400 y no avanza más. Una que pasa dispara dos eventos de Laravel: WebhookReceived para cada entrega y luego la clase propia del evento, como InventoryStockMoved:
Cada tipo de evento de la referencia de la API tiene una clase en StockShift\Laravel\Events, con $data tipado para PHPStan. WebhookReceived también se dispara para los tipos que esta versión del paquete no tiene como clase.

Escuchar con un atributo

Marca una clase o un método con #[OnStockShiftWebhook]:
En una clase llama a handle() o __invoke(). En un método llama a ese método. Indica varios tipos, o termina uno con * para coincidir con muchos, como sales.order.*. Tipa el parámetro como WebhookEvent cuando pueda recibir más de una clase. * solo escucha WebhookReceived. Se escanean las clases de app/Listeners. Cambia webhooks.discover en la configuración para escanear otras carpetas. Un tipo que no coincide con ningún evento falla al arrancar con una pista, para que un error de escritura no pase inadvertido. Un listener que el descubrimiento de eventos propio de Laravel ya encontró por su type hint no se agrega dos veces. Los listeners que implementan ShouldQueue se ponen en cola como de costumbre. php artisan optimize guarda en caché los listeners que encuentra. stockshift:cache y stockshift:clear hacen lo mismo por su cuenta.

Reintentos y colas

StockShift reintenta una entrega que no recibe un 2xx, y cada reintento conserva el id del evento. El paquete recuerda los ids durante un día y dispara los eventos una sola vez. Si un listener lanza una excepción, el id se olvida y la entrega recibe un 500, así que se procesa el reintento. Cambia webhooks.deduplicate_for para conservar los ids más tiempo, o ponlo en 0 para no conservar ninguno. Define STOCKSHIFT_WEBHOOK_QUEUE para responder a StockShift de inmediato y disparar los eventos desde un job en cola. STOCKSHIFT_WEBHOOK_QUEUE_CONNECTION elige la conexión de la cola.

Otras rutas

Pon webhooks.path en null para omitir la ruta y colocarla tú mismo. Una ruta para otra conexión revisa el secreto de esa conexión:
El middleware también funciona por separado, como stockshift.webhook o stockshift.webhook:eu. Pone el evento verificado en el atributo de solicitud stockshift.event. Las rutas de webhook no necesitan una excepción de CSRF porque no están en el grupo web. Para agregar middleware, como throttle:120,1, inclúyelo en webhooks.middleware.

Llamar a la API a mano

Http::stockshift() es una solicitud HTTP de Laravel con la URL base, el token y los tiempos de espera de la conexión. No reintenta ni lanza excepciones por errores:

Comandos de Artisan

Cada uno acepta --connection donde llama a la API. php artisan about muestra la conexión predeterminada y si los listeners están en caché.

Pruebas

StockShift::fake() responde a las llamadas con las respuestas que le das y registra cada una. Nada llega a la API y no hace falta un token:
Una clave es un método y una ruta después de la URL base. * coincide con cualquier cosa. Un valor es un cuerpo JSON, un código de estado, una respuesta PSR-7 o un closure que recibe el RecordedRequest y devuelve uno de esos. StockShift::page() construye una página de una lista con los registros bajo el nombre que usa la lista. Una llamada sin respuesta lanza UnexpectedRequest. Si llamas a fake() otra vez, agrega respuestas al mismo fake. RecordedRequest tiene connection, method, path, query, body, header() e idempotencyKey(). recorded() los devuelve todos, o los que coinciden con una ruta. assertNothingSent() comprueba que no se hizo ninguna llamada. Para probar tus listeners de webhook, envía una entrega firmada a través de la ruta:
dispatch() dispara los eventos sin la solicitud HTTP. payload() y signature() te dan el cuerpo y el encabezado si prefieres enviarlo tú mismo. Como las llamadas pasan por el cliente HTTP de Laravel, Http::fake() también funciona, si prefieres simular a nivel de HTTP.

Versiones

El paquete sigue el versionado semántico. Es compatible con las versiones de Laravel que reciben correcciones de seguridad y con las versiones de PHP que admite cada una. Una nueva versión mayor del SDK llega con una nueva versión mayor del paquete.