Este postes el Segundo de un curso completo y gratuito sobre Stripe, el cual está disponible tanto esta misma web así como en YouTube.
Tanto el curso como el código del mismo estan disponibles de forma abierta y gratuita, si te gusta el contenido ya sabes que puedes apoyar a la web haciendote premium, o comprando mi libro.
En el vídeo de YouTube, el contenido de este post incia en el minuto 40:06.
Índice
Stripe es una web muy poderosa y nos permite hacer gran cantidad de formas de pago como hemos visto en el capítulo anterior, pero no solo eso, también nos permite personalizar la pantalla de pago.
1 - Recibir pagos en Stripe
Cuando trabajamos con stripe tenemos varias formas de administrar la experiencia de usuario a través del checkout, o la pantalla de pago.
1.1 - Enlaces de pago
La primera opción es la más sencilla de todas, Stripe, cuando creas un producto, ya sea a través de la interfaz o de la API te permite asignarle un precio, una vez asignas este precio te permite crear un enlace de pago, el cual permite especificar qué campos quieres incluir en el formulario, como la dirección si es necesaria, por supuesto el método de pago, cantidad de elementos a vender, si es un pago único, identificador fiscal para empresas, etc.
También permite modificar a qué página el usuario será redirigido después de hacer el pago, que puede ser la de por defecto de Stripe o una de nuestra propia web.
En cualquier caso, al pulsar en el botón “crear enlace” de la parte superior Stripe nos dará una URL que es básicamente un enlace a la página de checkout de stripe.
La mayor pega de esta modalidad, es que no tiene nada de personalización sobre la interfaz, pero es la más fácil y rápida. Incluso si estas haciendo trabajos de forma freelance es muy sencillo decirle a tu cliente que te pague así, ya que puedes configurar dicho pago para que incluya una factura (por 2€ extra).
NOTA: Los enlaces de pago funcionan para pagar un único producto una vez.
1.2- Stripe checkout
Stripe checkout es la versión avanzada de lo anterior, como he indicado, los enlaces de pago funcionan para un único producto, pero qué pasa si tienes más?
Para ello puedes construir el checkout de forma dinámica desde tu código, y esta es la forma más popular de hacerlo.
Básicamente tu aplicación tiene que saber que es lo que el usuario necesita, y una vez tienes esa información creas una stripe checkout sesión en tu propio servidor, la cual va a ser redirigida a stripe para realizar un pago.
Este pago puede contener un único elemento o múltiples, además permite administrar suscripciones recurrentes o pagos futuros
Después cuando un usuario paga, Stripe manda un webhook a nuestra web, pero eso lo veremos más adelante.
Esta opción es muy buena y muy simple, la veremos como se hace más adelante.
1.3 - Stripe elements
En las dos versiones que acabamos de ver, todo el pago se administra en stripe, tanto la UI como la lógica están en stripe. Eso hace que no podamos personalizar la página de pago. Para muchos el no poder personalizar no es un problema, pero para empresas más consagradas sí lo es.
Por ello Stripe nos da una serie de componentes para nuestra interfaz, donde no tenemos que hacer nada en el lado servidor. En la interfaz utilizamos script the stripe.js y ponemos el HTML que vamos a copiar de la documentación:
Para esta opción stripe tiene una gran documentación la cual te dejo enlaza aquí; Así puedes tener una interfaz de pago totalmente personalizada pero que la gran mayoría del trabajo está realizado por el SDK de stripe.
1.4 - Utilizar la API de Stripe
La última opción es la más compleja pero también es la que más control nos da, y es utilizar la API de stripe; Puedes administrar todo el proceso, donde únicamente utilizas la API desde el backend.
Sinceramente esta opción me parece, además de ser la más compleja y complicada de administrar, la más peligrosa, ya que es muy probable que en algún momento del proceso los datos del cliente pasen por nuestro sistema, y almacenarlos puede traernos problemas.
Muy poca gente va a necesitar esta modalidad, es bueno mencionarla porque existe, pero requiere mucho trabajo.
2 - Registrar los productos en el sistema
Para no perder mucho tiempo en el código pues no es lo importante de este curso voy a resumir la forma de crear productos en nuestro sistema.
Al estar utilizando Entity Framework core con code first voy a crear una entidad llamada productos, estos productos contienen la siguiente estructura.
public class ProductEntity
{
public int Id { get; set; }
public string Name { get; set; }
public string ImageUrl { get; set; }
public decimal Price { get; set; }
public string StripePriceId { get; set; }
}
Donde, podemos observar que tenemos los elementos básicos como el id el nombre o el precio, pero además incluimos el ID de Stripe. Puedes pensar que este es el ID del producto. Pero en verdad no lo es, es el ID del precio, y es que en Stripe cada precio se entiende como una entidad diferente, por lo que el mismo ítem con dos precios distintos tendrá dos URL distintas.
Obviamente puedes cambiar el código para soportar tener múltiples precios en un único producto, pero no veo necesario implementar esta funcionalidad en nuestra aplicación.
Este modelo, lo tenemos que añadir al DbContext para poder acceder a él:
public class ApplicationDbContext : IdentityDbContext
{
public DbSet<ProductEntity> Products { get; set; }
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
}
Por supuesto debemos cambiar la conexión a la base de datos, la cual está ubicada en el appsettings, por la siguiente (si has utilizado el docker compose que mostré):
server=127.0.0.1;port=5306;database=bookplace;user=root;password=pass
Y utilizar MySql en vez de SQL cuando registramos el DbContext, para lo que tendrás que instalar el paquete “MySql.EntityFrameworkCore
”:
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseMySQL(connectionString)); 👈
Una vez tenemos esto, hay que borrar las migraciones previas (carpeta data -> migrations) , pues nuestra base de datos es MySQL y no SQLServer, personalmente recomiendo también eliminar el paquete de SqlServer
del csproj
.
Ahora creamos la nueva migración con:
dotnet ef migrations add InitialMigration
Debemos asegurarnos que nuestro código va a correr las migraciones, añadiendo la siguiente instrucción en el fichero startup:
using (var scope = app.Services.CreateScope())
{
ApplicationDbContext context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
context.Database.Migrate();
}
Y si ejecutas el código tendrás tu nueva base de datos junto a las tablas de usuarios y la nueva tabla de productos.
Lo que vamos a hacer posteriormente es añadir un producto, para ello simplemente he creado un caso de uso que inserta en la base de datos y un controlador API que lo llamara; Como es para test he ignorado el tema de seguridad, pero en una aplicación en producción debería estar incluida.
public record CreateProductRequest(string Name, string ImageUrl, decimal Price, string StripePriceId);
[Route("api/[controller]")]
[ApiController]
public class ProductsController(CreateProductUseCase createProduct) : ControllerBase
{
[HttpPost]
public async Task<bool> Create(CreateProductRequest request)
=> await createProduct.Execute(request);
}
public class CreateProductUseCase(ApplicationDbContext applicationDbContext)
{
public async Task<bool> Execute(CreateProductRequest createProduct)
{
//TODO: validations ETC but i'm not gonna waste time on that.
applicationDbContext.Products.Add(new Data.Entities.ProductEntity()
{
ImageUrl = createProduct.ImageUrl,
Name = createProduct.Name,
Price = createProduct.Price,
StripePriceId = createProduct.StripePriceId,
});
return await applicationDbContext.SaveChangesAsync() > 0;
Si ejecutamos una llamada como esta a la API, nos creará un registro en la base de datos:
{
"name": "producto1",
"ImageUrl" : "https://www.netmentor.es/images/book/guia_desarrollo_full_stack.jpg",
"price": "10.00",
"StripePriceId" : "price_1PHOvVCNTtv2rbe1aYNW9Hsk"
}
Y ahora únicamente debemos leerlo en el Front End.
3 - Crear enlaces de pago de forma dinámica
Pero la mejor forma de realizar pagos es a través de la sesión checkout que nos provee Stripe. Esta acción se puede hacer tanto a para un único ítem como para múltiples, y es la opción recomendada en la mayoría de casos.
La única diferencia a la hora de ser un único ítem o una lista es que vas a tener que implementar un carrito de la compra en el Front End, pero el resto del proceso debería ser el mismo.
Lo primero que debemos hacer es instalar el paquete “Strpie.net
” desde nuget, ya que para crear un enlace de pago vamos a utilizar el SDK de Stripe.
Depende que tipo de arquitectura vayas a utilizar en tu aplicación es posible que toda la comunicación con stirpe la vayas a hacer en una capa de infraestructura, pero si estás haciendo este curso para explorar, lo puedes hacer en el propio controlador sin ningún problema.
Para continuar tenemos que crear un controlador que tenga un método checkout. Este método va a invocar la API de stripe (a través de su SDK) para crear una checkout session:
StripeConfiguration.ApiKey = "keygoeshere";
SessionCreateOptions options = new SessionCreateOptions
{
SuccessUrl = "https://localhost:7265/payment-completed",
LineItems = new List<SessionLineItemOptions>
{
new SessionLineItemOptions
{
Price = stripePriceId,
Quantity = 1,
},
},
Mode = "payment",
};
SessionService service = new SessionService();
Session stripeSession = await service.CreateAsync(options);
En este código hay bastantes cosas que podemos tener en cuenta.
La primera es la API key, esta api key es nuestra key privada, por lo que no debéis mostrarla, en mi caso no importa mucho porque es la de test y antes de publicar este curso la voy a deshabilitar, pero recomiendo incluirla en los secrets de vuestra aplicación o en las variables de entorno del sistema.
La configuración es una clase estática, así que recomiendo poner la asignación de la key al inicio de la propia aplicación para hacerlo una única vez. (en el fichero program.cs)
Lo siguiente que podemos observar es el objeto SessionCreateOptions
, donde tenemos tres propiedades que vamos a indicar.
SuccessUrl
-> la Url donde vamos a devolver al usuario cuando un pago se completa correctamente. En nuestro caso es a localhost porque estamos testeando en local, te recomiendo que crees esa vista.
LineItems
-> Lista de los ítems que el usuario está comprando, como vemos vamos a enviar el id del precio que hemos obtenido anteriormente y el número de elementos, se puede observar que es una lista, así que si tienes un carrito de la compra puedes tener múltiples elementos aquí.
Mode
-> esta propiedad indica que modo de checkout, básicamente si es un pago único (payment) una subscripción (subscription) o si vas a guardar los datos de usuario pero pagar después, por ejemplo una prueba de 7 días (setup).
El objeto tiene muchas más propiedades, pero estas son la clave a la hora de crear pagos.
La última línea de este snippet nos devuelve un objeto Session, este objeto tiene una URL a la cual debemos redireccionar al usuario
Si rellenas los datos correctamente, una vez se realiza el pago Stripe redireccionará al usuario a la vista que has indicado (payment-completed).
Y podemos ver el pago en la propia interfaz de Stripe:
Pero claro, llegados a este punto el usuario ha pagado y no ha recibido el producto, de hecho ni siquiera sabemos que hemos recibido el pago. Aquí es donde entran los webhooks.