Login to bookmark this video
Buy Access to Course
08.

Centralizar la lógica empresarial de LemonSqueezy

|

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

Hasta ahora, nos hemos centrado en poner en marcha nuestra caja, ¡y lo hemos conseguido! Pero nuestro código está un poco disperso por el controlador. ¿No sería mucho más cómodo tener todo lo relacionado con la API de LemonSqueezy en una clase aparte? ¡Claro que sí! ¡Así que vamos a organizarnos!

Tenemos que localizar todo el código relacionado con LemonSqueezy en el controlador y trasladarlo a un servicio independiente para que sea más fácil de mantener, reutilizar y probar. Para ello, en src/Store/, crea una nueva clase llamada LemonSqueezyApi. Conviértela enfinal readonly. Ahora podemos mover nuestro método createLsCheckoutUrl(). Copiaré este gran bloque, lo eliminaré y lo pegaré en nuestra nueva clase, y esta vez lo llamaré public. Como sabemos que está relacionado con LemonSqueezy porque está enLemonSqueezyApi, podemos cambiarle el nombre a createCheckoutUrl() para que sea más sencillo.

78 lines | src/Store/LemonSqueezyApi.php
// ... lines 1 - 9
final readonly class LemonSqueezyApi
{
public function createCheckoutUrl(HttpClientInterface $lsClient, ShoppingCart $cart, ?User $user): string
{
if ($cart->isEmpty()) {
throw new \LogicException('Nothing to checkout!');
}
$products = $cart->getProducts();
$variantId = $products[0]->getLsVariantId();
$attributes = [];
if ($user) {
$attributes['checkout_data']['email'] = $user->getEmail();
// ... line 24
}
// ... lines 26 - 75
}
}

A continuación, vamos a coger $lsClient y $cart, y encima, convertirlos en dependencias del constructor con public function __construct(). Pega, también simplificaremos $lsClient y lo llamaremos simplemente $client. Por encima de este argumento, añade#[Target('lemonSqueezyClient')], añade private antes de cada propiedad.

90 lines | src/Store/LemonSqueezyApi.php
// ... lines 1 - 9
use Symfony\Contracts\HttpClient\HttpClientInterface;
// ... line 11
final readonly class LemonSqueezyApi
{
public function __construct(
#[Target('lemonSqueezyClient')]
private HttpClientInterface $client,
private ShoppingCart $cart,
// ... lines 18 - 20
) {
}
// ... lines 23 - 88
}

Y por último, cambia esta variable $cart por una propiedad con $this->cart. Haremos lo mismo con el resto de variables $cart. Y ya que estamos aquí, también cambiaremos $lsClient por $this->client. Bien.

90 lines | src/Store/LemonSqueezyApi.php
// ... lines 1 - 23
public function createCheckoutUrl(?User $user): string
{
if ($this->cart->isEmpty()) {
throw new \LogicException('Nothing to checkout!');
}
// ... line 29
$products = $this->cart->getProducts();
// ... lines 31 - 37
if (count($products) === 1) {
// ... lines 39 - 44
} else {
$attributes['custom_price'] = $this->cart->getTotal();
// ... lines 47 - 57
}
// ... lines 59 - 61
$response = $this->client->request(Request::METHOD_POST, 'checkouts', [
// ... lines 63 - 87
}
// ... lines 89 - 90

Ahora necesitamos un servicio para generar URLs. Podemos inyectarlo en el constructor con UrlGeneratorInterface $urlGenerator. A continuación, sustituye $this->generateUrl()por $this->urlGenerator->generate().

90 lines | src/Store/LemonSqueezyApi.php
// ... lines 1 - 8
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
// ... lines 10 - 11
final readonly class LemonSqueezyApi
{
public function __construct(
// ... lines 15 - 17
private UrlGeneratorInterface $urlGenerator,
// ... lines 19 - 20
) {
}
// ... line 23
public function createCheckoutUrl(?User $user): string
{
// ... lines 26 - 59
$attributes['product_options']['redirect_url'] = $this->urlGenerator->generate('app_order_success', [], UrlGeneratorInterface::ABSOLUTE_URL);
// ... lines 61 - 87
}
}

También necesitamos acceder a los parámetros. Podríamos inyectar todo el servicio ParameterBagInterface, que nos permite acceder a cualquier parámetro, pero como sólo necesitamos uno - storeId - vamos a inyectarlo directamente.

En nuestro constructor, añade: private readonly string $storeId,. Encima, añade el atributo PHP con #[Autowire('%env(LEMON_SQUEEZY_STORE_ID)%')]. Y por último, sustituye cada instancia de $this->getParameter() por $this->storeId. Sólo lo veo una vez aquí, así que es bastante fácil.

90 lines | src/Store/LemonSqueezyApi.php
// ... lines 1 - 11
final readonly class LemonSqueezyApi
{
public function __construct(
// ... lines 15 - 18
#[Autowire('%env(LEMON_SQUEEZY_STORE_ID)%')]
private string $storeId,
) {
}
// ... line 23
public function createCheckoutUrl(?User $user): string
{
// ... lines 26 - 61
$response = $this->client->request(Request::METHOD_POST, 'checkouts', [
'json' => [
'data' => [
// ... lines 65 - 66
'relationships' => [
'store' => [
'data' => [
// ... line 70
'id' => $this->storeId,
],
],
// ... lines 74 - 79
],
],
],
]);
// ... lines 84 - 87
}
}

Ahora, de vuelta en OrderController::checkout(), deshagámonos de estas dependencias no utilizadas e inyectemos LemonSqueezyApi $lsApi en su lugar. A continuación, utiliza el servicio con $lsCheckoutUrl = $lsApi->createCheckoutUrl();.

91 lines | src/Controller/OrderController.php
// ... lines 1 - 17
class OrderController extends AbstractController
{
// ... lines 20 - 59
#[Route('/checkout', name: 'app_order_checkout')]
public function checkout(
LemonSqueezyApi $lsApi,
// ... line 63
): Response {
$lsCheckoutUrl = $lsApi->createCheckoutUrl($user);
return $this->redirect($lsCheckoutUrl);
}
// ... lines 69 - 89
}

¡Hora de probar! Asegurémonos de que aún podemos pagar. En nuestro sitio, recarga, selecciona "Limonada Clásica", añade una al carrito y haz clic en "Pagar con LemonSqueezy". ¡Sí! Estamos en la página de pago de LemonSqueezy y todo parece perfecto

Vale, ahora que sabemos que la compra funciona, ¿podemos hacer que $lsStoreUrl en el métodosuccess() sea dinámico? ¡Pues sí! Y LemonSqueezy tiene una ruta API precisamente para eso En los documentos de la API, busca la ruta "Recuperar una tienda"... y mira la respuesta de ejemplo de la derecha. Parece que podemos leer la URL desde attributes, así que, de vuelta a nuestro código, enLemonSqueezyApi, crea un nuevo método público. Llámalo retrieveStoreUrl(), y haz que devuelva un string. Dentro, añade$response = $this->client->request(Request::METHOD_GET, 'stores/' . $this->storeId). Debajo, escribe $lsStore = $response->toArray() y, por último,return $lsStore['data']['attributes']['url']:

98 lines | src/Store/LemonSqueezyApi.php
// ... lines 1 - 11
final readonly class LemonSqueezyApi
{
// ... lines 14 - 89
public function retrieveStoreUrl(): string
{
$response = $this->client->request(Request::METHOD_GET, 'stores/' . $this->storeId);
$lsStore = $response->toArray();
return $lsStore['data']['attributes']['url'];
}
}

De vuelta en el método success(), inyecta LemonSqueezyApi $lsApi, y sustituye esta URL codificada por $lsStoreUrl = $lsApi->retrieveStoreUrl().

92 lines | src/Controller/OrderController.php
// ... lines 1 - 17
class OrderController extends AbstractController
{
// ... lines 20 - 69
#[Route('/checkout/success', name: 'app_order_success')]
public function success(
// ... lines 72 - 74
): Response
{
// ... line 77
$lsStoreUrl = $lsApi->retrieveStoreUrl();
// ... lines 79 - 89
}
}

¡Es hora de hacer otra prueba! De vuelta en nuestro sitio, elige una de nuestras deliciosas limonadas -esta vez elegiré manzana- y añádela al carrito. En la página del carrito, haz clic de nuevo en el botón "Realizar pedido", rellena nuestras credenciales y la dirección de facturación, haz clic en "Pagar" y, finalmente, en el modal "correcto", haz clic en "Continuar". ¡Listo! ¡Aquí está nuestro mensaje flash! ¡Sigue funcionando!

A continuación: Asignemos un cliente de LemonSqueezy al usuario correspondiente en nuestro sistema para saber qué compras ha realizado.