← Volver ao blog

Integrar pagamentos con Stripe na web dun cliente: o que non conta a documentación

Daniel García·

Ilustración del artículo: Integrar pagamentos con Stripe na web dun cliente: o que non conta a documentación

Hai un par de meses un cliente que vende menaxe de cociña online escribiunos un venres ás seis da tarde: “necesitamos ter Stripe funcionando o luns, xa anunciamos a data de lanzamento por email a toda a lista”. Dito así parece traballo dunha tarde: tes a API, tes a documentación, metes as claves e xa está. Non foi así. O que nos comeu a fin de semana non foi o checkout, foron os webhooks.

Integrar pagamentos con Stripe nunha tenda pequena non é difícil técnicamente: o SDK é bo, e Checkout ou Payment Element cobren o 90% dos casos sen que escribas unha liña de HTML dun formulario de tarxeta. A parte que ninguén conta é que o pagamento “funciona” no navegador moito antes de que o teu backend saiba que pasou algo; se confías só no que devolve o frontend, tarde ou cedo vas ter pedidos cobrados que non quedan marcados como pagados na túa base de datos.

O problema concreto foi este: configuramos o webhook de checkout.session.completed para marcar o pedido como pagado e disparar o email de confirmación. Funcionou perfecto nas probas. En produción, Stripe reintenta o mesmo evento se o teu endpoint non responde 200 a tempo, e o noso, os primeiros días, tardaba de máis porque mandaba o email de forma síncrona antes de responder. Resultado: un cliente recibiu tres emails de confirmación para un só pedido, e nos logs vimos o mesmo event.id procesado dúas veces porque non gardabamos en ningures os ids xa vistos. Nada roto de cara ao diñeiro, Stripe non cobra dúas veces por iso, pero si de cara á credibilidade: o dono da tenda chamounos preocupado pensando que lle cobraramos de máis.

O lío non é o checkout, son os webhooks

A solución foi aburrida, e así ten que ser: gardar o event.id nunha táboa con índice único antes de procesar nada, responder 200 en milisegundos, e mover o email a unha cola en segundo plano. Lin a política de reintentos de Stripe, ata tres días insistindo cun evento fallido, para entender que un endpoint lento é indistinguible dun caído desde o seu lado. Se a túa integración non é idempotente desde o primeiro día, vas ter este bug tarde ou cedo; non é unha posibilidade remota, é case un cando.

O alcance PCI que ninguén firma no contrato

O outro que descolocou ao cliente foi o cumprimento PCI DSS. El asumía que, como usabamos Stripe, “o cumprimento lévao Stripe”. É certo a medias: se o número de tarxeta nunca toca o teu servidor, o teu alcance redúcese ao cuestionario SAQ A, o máis curto que existe. Pero no seu provedor anterior, un TPV bancario de toda a vida, alguén lle prometera integrar o formulario de tarxeta directamente na web “para que non se note que sae a outra páxina”, e isto tería disparado o alcance a un SAQ D con auditoría anual. Tivemos que explicarlle, con calculadora na man, que o aforro visual non compensaba o custo de cumprimento. Ao final usamos Payment Element, tokenizado por Stripe.js, para que a pantalla quede dentro da súa web sen que o dato da tarxeta pase nunca polo noso servidor.

Isto ten que ver con algo do que xa falamos ao presupostar un proxecto web: o cliente paga polo que ve, e o risco que non ve é xusto o que máis tarde explota. Un paralelismo parecido ao da dependencia que tombou un checkout enteiro: nos dous casos o fallo non estaba na lóxica de negocio que todo o mundo revisa, senón na canalización que se dá por feito que sempre funciona.

Seguimos usando Stripe en case todos os proxectos de ecommerce que levamos, e seguiríao recomendando sen dubidalo. Pero cada vez que alguén me pide que estea listo para o luns, penso nesos tres emails duplicados e na cara que puxo o cliente ao teléfono. ¿Cantas integracións de pagamento en produción por aí fóra non son realmente idempotentes e simplemente non lles tocou aínda o reintento que o destape?

stripepagamentosintegraciónsecommerce