Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: CI

on:
push:
branches:
- main
pull_request:
workflow_dispatch:

permissions:
contents: read

jobs:
tests:
name: PHP ${{ matrix.php-version }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
php-version: ['8.2', '8.4']

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
coverage: none
tools: composer:v2

- name: Install dependencies
run: composer install --no-interaction --prefer-dist

- name: Run test suite
run: ./vendor/bin/phpunit --configuration phpunit.xml
16 changes: 8 additions & 8 deletions .github/workflows/deploy-on-main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@ jobs:
run: |
set -euo pipefail

if [[ ! -f VERSION ]]; then
echo "Missing VERSION file"
if [[ ! -f VERSION.md ]]; then
echo "Missing VERSION.md file"
exit 1
fi

LOCAL_VERSION="$(head -n1 VERSION | tr -d '[:space:]')"
RELEASE_BODY="$(tail -n +2 VERSION || true)"
LOCAL_VERSION="$(head -n1 VERSION.md | tr -d '[:space:]')"
RELEASE_BODY="$(tail -n +2 VERSION.md || true)"
if [[ ! "$LOCAL_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$ ]]; then
echo "VERSION must be valid semver. Got: $LOCAL_VERSION"
echo "VERSION.md first line must be valid semver. Got: $LOCAL_VERSION"
exit 1
fi

Expand Down Expand Up @@ -61,13 +61,13 @@ jobs:

HIGHEST="$(printf '%s\n%s\n' "$REMOTE_MAX" "$LOCAL_VERSION" | sort -V | tail -n1)"
if [[ "$HIGHEST" != "$LOCAL_VERSION" ]]; then
echo "Local VERSION is not greater than remote max tag"
echo "Local VERSION.md is not greater than remote max tag"
echo "should_deploy=false" >> "$GITHUB_OUTPUT"
exit 0
fi

if ! printf '%s' "$RELEASE_BODY" | grep -q '[^[:space:]]'; then
echo "VERSION requires release notes after the first line when publishing."
echo "VERSION.md requires release notes after the first line when publishing."
echo "Example:"
echo " 3.6.1"
echo " ## What changed"
Expand Down Expand Up @@ -108,4 +108,4 @@ jobs:
- name: Deploy skipped
if: steps.version.outputs.should_deploy != 'true'
run: |
echo "Skipping deploy: local VERSION is not greater than remote semver tag."
echo "Skipping deploy: local VERSION.md is not greater than remote semver tag."
192 changes: 192 additions & 0 deletions README.es.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
# Facturapi PHP SDK

SDK oficial de PHP para [Facturapi](https://www.facturapi.io).

Idioma: Español | [English](./README.md)

[![Última Versión](https://img.shields.io/packagist/v/facturapi/facturapi-php?style=flat-square)](https://packagist.org/packages/facturapi/facturapi-php)
[![Versión de PHP](https://img.shields.io/packagist/php-v/facturapi/facturapi-php?style=flat-square)](https://packagist.org/packages/facturapi/facturapi-php)
[![Descargas Totales](https://img.shields.io/packagist/dt/facturapi/facturapi-php?style=flat-square)](https://packagist.org/packages/facturapi/facturapi-php)
[![Descargas Mensuales](https://img.shields.io/packagist/dm/facturapi/facturapi-php?style=flat-square)](https://packagist.org/packages/facturapi/facturapi-php)
[![Licencia](https://img.shields.io/packagist/l/facturapi/facturapi-php?style=flat-square)](https://packagist.org/packages/facturapi/facturapi-php)

## Instalación ⚡

```bash
composer require facturapi/facturapi-php
```

Sin Composer (workaround soportado):

```php
require_once __DIR__ . '/path/to/facturapi-php/src/Facturapi.php';
```

Requisitos:
- PHP `>=8.2`

## Inicio Rápido 🚀

```php
<?php

require_once __DIR__ . '/vendor/autoload.php';

use Facturapi\Facturapi;

$apiKey = getenv('FACTURAPI_KEY') ?: 'YOUR_API_KEY';
$facturapi = new Facturapi($apiKey);

$customer = $facturapi->Customers->create([
'email' => 'walterwhite@gmail.com',
'legal_name' => 'Walter White',
'tax_id' => 'WIWA761018',
'address' => [
'zip' => '06800',
'street' => 'Av. de los Rosales',
'exterior' => '123',
'neighborhood' => 'Tepito',
],
]);
```

## Configuración del Cliente ⚙️

Firma del constructor:

```php
new Facturapi(string $apiKey, ?array $config = null)
```

Claves soportadas en `config`:
- `apiVersion` (`string`, valor por defecto: `v2`)
- `timeout` (`int|float`, valor por defecto: `360` segundos)
- `httpClient` (`Psr\Http\Client\ClientInterface`, avanzado)

Ejemplo:

```php
use Facturapi\Facturapi;

$facturapi = new Facturapi($apiKey, [
'apiVersion' => 'v2',
'timeout' => 420,
]);
```

### Cliente HTTP Personalizado (Avanzado)

El SDK funciona sin configuración adicional con su cliente interno basado en Guzzle.

Si proporcionas `httpClient`, puedes pasar cualquier cliente compatible con PSR-18 y configurar ahí mismo los timeouts:

```php
use Facturapi\Facturapi;
use GuzzleHttp\Client;

$httpClient = new Client([
'timeout' => 420,
]);

$facturapi = new Facturapi($apiKey, [
'httpClient' => $httpClient,
]);
```

## Uso Común 🧾

### Crear un Producto

```php
$product = $facturapi->Products->create([
'product_key' => '4319150114',
'description' => 'Apple iPhone 8',
'price' => 345.60,
]);
```

### Crear una Factura

```php
$invoice = $facturapi->Invoices->create([
'customer' => 'YOUR_CUSTOMER_ID',
'items' => [[
'quantity' => 1,
'product' => 'YOUR_PRODUCT_ID',
]],
'payment_form' => \Facturapi\PaymentForm::EFECTIVO,
'folio_number' => '581',
'series' => 'F',
]);
```

### Descargar Archivos

```php
$zipBytes = $facturapi->Invoices->downloadZip('INVOICE_ID');
$pdfBytes = $facturapi->Invoices->downloadPdf('INVOICE_ID');
$xmlBytes = $facturapi->Invoices->downloadXml('INVOICE_ID');
```

`downloadPdf()` devuelve bytes crudos de PDF (cadena binaria), no base64.

```php
file_put_contents('invoice.pdf', $pdfBytes);
```

### Enviar por Correo

```php
$facturapi->Invoices->sendByEmail('INVOICE_ID');
```

### Catálogos de Comercio Exterior

```php
$results = $facturapi->ComercioExteriorCatalogs->searchTariffFractions([
'q' => '0101',
'page' => 0,
'limit' => 10,
]);
```

## Manejo de Errores ⚠️

En respuestas no-2xx, el SDK lanza `Facturapi\Exceptions\FacturapiException`.

La excepción incluye:
- `getMessage()`: mensaje del API cuando está disponible.
- `getStatusCode()`: código HTTP.
- `getErrorData()`: payload JSON decodificado del error (shape completo del API).
- `getRawBody()`: cuerpo crudo de la respuesta.

```php
use Facturapi\Exceptions\FacturapiException;

try {
$facturapi->Invoices->create($payload);
} catch (FacturapiException $e) {
$status = $e->getStatusCode();
$error = $e->getErrorData(); // Shape completo del error del API cuando el body es JSON válido.
$firstDetail = $error['details'][0] ?? null; // p.ej. ['path' => 'items.0.quantity', 'message' => '...', 'code' => '...']
}
```

## Notas de Migración (v4) 🔄

- La versión mínima de PHP ahora es `>=8.2`.
- Se eliminó el soporte para el argumento posicional `apiVersion` en el constructor.
- Proyectos con Composer: no requieren cambios de carga; continúen usando `vendor/autoload.php`.
- Proyectos sin Composer pueden seguir usando el SDK cargando `src/Facturapi.php` directamente.
- Los aliases snake_case están deprecados en v4 y se eliminarán en v5.
- `Facturapi\\Exceptions\\Facturapi_Exception` está deprecada en v4 y se eliminará en v5.
- Usa `Facturapi\\Exceptions\\FacturapiException`.

## Documentación 📚

Documentación completa: [https://docs.facturapi.io](https://docs.facturapi.io)

## Soporte 💬

- Issues: abre un issue en GitHub
- Email: `contacto@facturapi.io`
Loading
Loading