Login to bookmark this video
Buy Access to Course
15.

Mejorar la gestión de errores de la API

|

Share this awesome video!

|

Keep on Learning!

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Dirígete a src/Store/LemonSqueezyApi.php. Probablemente recuerdes este métodocreateCheckoutUrl() de antes. Este lanzamiento a string solucionaba un error

127 lines | src/Store/LemonSqueezyApi.php
// ... lines 1 - 11
final readonly class LemonSqueezyApi
{
// ... lines 14 - 23
public function createCheckoutUrl(User $user): string
{
// ... lines 26 - 35
$attributes['checkout_data']['custom']['user_id'] = $user->getId();
// ... lines 37 - 86
}
// ... lines 88 - 125
}

Elimínalo temporalmente para que podamos recuperar ese error. De vuelta en tu navegador, haz clic en "Añadir a la cesta", luego en "Pagar con LemonSqueezy", y... veremos nuestro esperadoClientException.

Anteriormente, utilizamos este truco de dd($response->getContent(false)) para ver los detalles detrás de ClientException. Descomenta esta línea, actualiza la página y... ahora podemos ver el error real.

Hacer que los mensajes de error sean más informativos

Esto está bien, pero seguro que podríamos mejorarlo aún más. En lugar de utilizar dd()para depurar, probemos a envolver el método client->request() en otro método. En la parte inferior de esta clase, crea un private function llamado request()que devuelva un array.

131 lines | src/Store/LemonSqueezyApi.php
// ... lines 1 - 126
private function request(string $method, string $url, array $options = []): array
{
}
// ... lines 130 - 131

Su primer argumento será string $method, seguido de string $url y una matriz de opciones. Ahora viene la parte divertida: Abre un bloque try-catch y, en el try, escribe $response = $this->client->request() y pasa todas las variables - $method, $url, y $options. Crea una variable $data que sea igual a $response->toArray(). Nosotros catch ClientException $e .

140 lines | src/Store/LemonSqueezyApi.php
// ... lines 1 - 127
private function request(string $method, string $url, array $options = []): array
{
try {
$response = $this->client->request($method, $url, $options);
$data = $response->toArray();
} catch (ClientException $e) {
}
// ... lines 136 - 137
}
// ... lines 139 - 140

En la parte inferior, return $data, y de vuelta en el catch, queremos el contenido en bruto de la respuesta, así que escribe $data = $e->getResponse()->toArray() y pasa false como primer argumento. También añadiremos aquí dd($data) temporalmente para que podamos ver la respuesta de error de la API.

141 lines | src/Store/LemonSqueezyApi.php
// ... lines 1 - 127
private function request(string $method, string $url, array $options = []): array
{
try {
// ... lines 131 - 132
} catch (ClientException $e) {
$data = $e->getResponse()->toArray(false);
dd($data);
}
return $data;
}
// ... lines 140 - 141

A continuación, actualiza el método createCheckoutUrl(). En lugar de$this->client->request(), utiliza sólo $this->request(), pasando todos los mismos argumentos. Si nos dirigimos e intentamos comprobarlo de nuevo... ¡boom! Esto es un volcado correcto de la petición real a la API en forma de matriz.

141 lines | src/Store/LemonSqueezyApi.php
// ... lines 1 - 24
public function createCheckoutUrl(User $user): string
{
// ... lines 27 - 61
$response = $this->request(Request::METHOD_POST, 'checkouts', [
// ... lines 63 - 82
]);
// ... lines 84 - 87
}
// ... lines 89 - 141

Crear mensajes de error útiles

Bien, en lugar de "volcar y morir", vamos a elaborar algunos mensajes de error que sean más útiles. En nuestro código, busca el método request(). Comenta esta sentencia dd()y debajo, añade $mainErrorMessage = 'LS API Error:'. Ahora, comprobemos si tenemos un error con $error = $data['errors'][0] ?? null. Si lo hay,error, haz otra comprobación con if (isset($error['status']). Dentro, escribe $mainErrorMessage .= ' ' . $error['status']. Haz lo mismo con title,detail, y source.pointer. Iré más rápido en esta parte. Por último, else, y dentro, añade el contenido en bruto con$mainErrorMessage .= $e->getResponse()->getContent(false). Perfecto

Al final, throw new \Exception() con $mainErrorMessage, 0como segundo argumento, y $e como tercer argumento.

163 lines | src/Store/LemonSqueezyApi.php
// ... lines 1 - 127
private function request(string $method, string $url, array $options = []): array
{
try {
// ... lines 131 - 132
} catch (ClientException $e) {
// ... lines 134 - 136
$mainErrorMessage = 'LS API Error:';
$error = $data['errors'][0] ?? null;
if ($error) {
if (isset($error['status'])) {
$mainErrorMessage .= ' ' . $error['status'];
}
if (isset($error['title'])) {
$mainErrorMessage .= ' ' . $error['title'];
}
if (isset($error['detail'])) {
$mainErrorMessage .= ' "' . $error['detail'] . '"';
}
if (isset($error['source']['pointer'])) {
$mainErrorMessage .= sprintf(' (at path "%s")', $error['source']['pointer']);
}
} else {
$mainErrorMessage .= $e->getResponse()->getContent(false);
}
throw new \Exception($mainErrorMessage, 0, $e);
}
// ... lines 159 - 160
}
// ... lines 162 - 163

Esto establece la excepción original como la anterior, lo que ayuda aún más a la depuración ¡Eso es! Se trata de un patrón bastante común y útil para simplificar excepciones complejas, pero sin dejar de proporcionar una referencia a la original.

¡Vamos a probarlo!

En la página de pago, actualiza y... ¡voilá! El mensaje de error genérico es ahora un mensaje personalizado:

Error de API LS: 422 Entidad no procesable "El campo {0} debe ser una cadena" (en la ruta "data/attributes/checkout_data/custom/user_id").

Eso era mucho más fácil de entender.

Todo lo que tenemos que hacer ahora es devolver la tipificación string en la línea user_id. Ya no necesitamos esta línea $response->toArray(), así que podemos eliminarla junto con la dd(). Sustituye también la variable $response por $lsCheckout, puesto que ya tenemos aquí una matriz de datos de objetos de caja.

161 lines | src/Store/LemonSqueezyApi.php
// ... lines 1 - 24
public function createCheckoutUrl(User $user): string
{
// ... lines 27 - 36
$attributes['checkout_data']['custom']['user_id'] = (string) $user->getId();
// ... lines 38 - 61
$lsCheckout = $this->request(Request::METHOD_POST, 'checkouts', [
// ... lines 63 - 82
]);
// ... lines 84 - 85
}
// ... lines 87 - 161

Vuelve a actualizar la página para ver si funciona, y... ¡ya está!

El último paso es sustituir todas las llamadas a $this->client->request() restantes por$this->request(). Haré esto rápidamente para retrieveStoreUrl(),listOrders(), y eliminaré las llamadas a $response->toArray() mientras estoy en ello.

156 lines | src/Store/LemonSqueezyApi.php
// ... lines 1 - 12
final readonly class LemonSqueezyApi
{
// ... lines 15 - 87
public function retrieveStoreUrl(): string
{
$lsStore = $this->request(Request::METHOD_GET, 'stores/' . $this->storeId);
return $lsStore['data']['attributes']['url'];
}
// ... line 94
public function listOrders(User $user): array
{
// ... lines 97 - 102
return $this->request(Request::METHOD_GET, 'orders', [
// ... lines 104 - 112
]);
}
public function retrieveCustomer(string $customerId): array
{
return $this->request(Request::METHOD_GET, 'customers/' . $customerId);
}
// ... lines 120 - 154
}

Si probamos nuestro sitio una vez más... ¡la página de cuenta sigue funcionando... y también la página de pago! Nuestro proceso de tratamiento de errores es ahora eficaz e informativo.

Siguiente paso: Mejoremos la experiencia de pago de nuestros clientes incrustando la página de pago de LemonSqueezy en nuestra aplicación.