Stripe es una startup fundada en 2010 por John and Patrick Collison cuyo objetivo era simplificar las transacciones online y el flujo de interfaces de usuarios empleado por las pasarelas actuales. Desde luego lo han conseguido, ya que es una gran realidad que los pagos online a través de la web a veces pueden llegar a ser complejos para el usuario.
Personalmente he tenido que emplear esta plataforma en uno de los últimos proyectos desarrollados en mi empresa y he obtenido una muy buena impresión de esta herramienta ya que proporciona cantidad de funcionalidades y todas ellas muy potentes. Con una gran documentación muy detallada para los desarrolladores integrar su pasarela de pago es bastante sencillo. Como otros pasarelas de pago no debemos de saltar por su interfaz si no que podemos incluirla en nuestra propia interfaz, una gran ventaja para el UX.
Arrancar con Stripe
Arrancar con Stripe es bastante sencillo, en poco tiempo tienes montada la pasarela y puedes estar aceptando pagos. El precio del uso de la herramienta es bastante aceptable ya que ellos se llevan un pequeño porcentaje de cada transacción y realizan descuentos a partir de ciertos volúmenes. Además acepta la mayoría de las tarjetas de créditos como Visa, Master Card o American Express.
Lo que mas me llamó la atención es la interfaz que tienen para ver toda la información acerca de los pagos de tu herramienta. Con dos entornos de trabajo uno en producción y otro de test puedes realizar todo el seguimiento de los cobros . El panel te muestra información de los pagos realizados, de los usuarios que han empleado la pasarela y su información asociada a ellas y puedes gestionar las transferencias de los beneficios obtenidos.
Una gran funcionalidad que proporciona es crear planes de subscripción de tal manera que los usuarios de tu aplicación pueden asociarse a un plan el cual es altamente configurable y que el cobro sea recurrente por cada periodo de renovación. La mayoría de aplicaciones actuales basadas en planes de subscripción evitan gran parte del desarrollo ya que Stripe te lo proporciona todo. Además se pueden generar cupones descuento.
Además toda la información que ocurre a través de tu plataforma será registrada en un log que puedes consultar en cualquier momento, además de ver todos los eventos que se lanzan cuando algo ocurre en Stripe. Además Stripe expone una forma de escuchar estos eventos para realizar las tareas pertinentes en nuestra aplicación, para ello emplean los webhooks, los cuales veremos un ejemplo mas adelante.
La parte frontend es altamente customizable, pero Stripe proporciona una librería checkout.js que nos proporciona un modal para introducir nuestra tarjeta de crédito, la cual contiene todo tipo de validaciones. Además podemos Checkout (https://stripe.com/docs/checkout#integration-simple)
Ejemplo Práctico
Aquí podemos ver un ejemplo de un formulario básico que incluye dicho Checkout el cual podemos configurar a través de diversas opciones y definir sobre que ruta queremos que se lea dicho formulario para realizar las opciones pertinentes.
<form action="{{path('checkout_charge')}}" method="post"> <script src="https://checkout.stripe.com/checkout.js" class="stripe-button" data-key="{{publisheableKey}}" data-name="Nombre compañía" data-image="imagen.jpg" data-amount="990" data-currency="EUR" data-description="Description" data-email="{{app.user.email}}" data-label="Label" data-allow-remember-me="false" data-panel-label="Subscripción"></script> </form>
Cuando el usuario rellena dicho formulario, Stripe automáticamente genera un token para dicho pago. A nivel interno para cada usuario de la aplicación almaceno el CustomerId que Stripe nos proporciona. Por ello es tan fácil como proporcionarle a Stripe el email y el token asociado para verificar si el primer cargo contra la plataforma ha podido realizarse. De esta manera si la transacción ha sido exitosa se asocia al usuario este customerId para futuras transacciones y en caso contrario, devolver una página donde informar al usuario del fracaso de la transacción.
public function chargeAction(Request $request) { $em=$this->getDoctrine()->getManager(); $stripe=$this->get('stripe'); $token= $request->request->get('stripeToken'); $user = $this->get('security.context')->getToken()->getUser(); // get customer if($user->getStripeCustomerId()== null){ $customer=$stripe->createStripeCustomer($user->getEmail(),$token); if(!$customer) return $this->render('EjemploBundle:Checkout:charge_ko.html.twig', array()); $user->setStripeCustomerId($customer->id); } $em->persist($user); $em->flush(); //subscribe $subscription=$stripe->subscribePlanCustomer($customer->id); if(!$subscription) return $this->render('EjemploBundle:Checkout:charge_ko.html.twig', array()); $user->setSubscriptionId($subscription->id); $em->persist($user); $em->flush(); // enviar mail return $this->render('EjemploBundle:Checkout:charge.html.twig', array( 'subscription' => $subscription, )); }
Este código se apoya en un servicio el cual se comunica con stripe a través de dos tokens que nos proporciona la propia herramienta (secretkey y publisheable key) para verificar que eres tu el que realiza las transacciones. Empleando la librería de php (https://github.com/stripe/stripe-php) es tan sencilla la comunicación con la API como el código que veréis a continuación. La gran ventaja que tiene esta herramienta es que tiene librerías para todo tipo de lenguajes además de integrarse con android y iphone. Puedes tener tu plataforma de pagos integrada en cualquiera de tus aplicaciones en cualquier lenguaje y plataforma. Aquí tenéis la url donde proporciona toda la información de estas librerías (https://stripe.com/docs/libraries)
A continuación os dejo una clase Stripe que encapsula funcionalidades básicas de comunicación con Stripe
class Stripe{ protected $container; private $secretKey; private $publisheableKey; public function __construct(\Symfony\Component\DependencyInjection\Container $container,$secretKey,$publisheableKey) { $this->container = $container; $this->secretKey=$secretKey; $this->publisheableKey=$publisheableKey; \Stripe::setApiKey($this->secretKey); } public function getPublisheableKey() { return $this->publisheableKey; } public function setPublisheableKey($publisheableKey) { $this->publisheableKey = $publisheableKey; } public function createStripeCustomer($email,$token) { try { $customer = \Stripe_Customer::create(array( 'email' => $email, 'card' => $token )); return $customer; } catch(\Stripe_CardError $e) { return false; } } public function getStripeCustomer($customerId) { $customer = \Stripe_Customer::retrieve($customerId); return $customer; } public function getStripeEvent($eventId) { try{ $event = \Stripe_Event::retrieve($eventId); }catch(\Stripe_InvalidRequestError $e){ return false; } return $event; } public function updateCardCustomer($customerId,$token) { $customer=$this->getStripeCustomer($customerId); try{ $customer->card = $token; $customer->save(); } catch(\Stripe_CardError $e) { return false; } return $customer; } public function subscribePlanCustomer($customerId) { $customer=$this->getStripeCustomer($customerId); $planId=$this->container->getParameter('stripe_plan_name'); try { $subscription=$customer->subscriptions->create(array('plan'=>$planId)); } catch(\Stripe_CardError $e) { return false; } return $subscription; } public function getSubscribeInformation($customerId,$subscribeId) { $customer=$this->getStripeCustomer($customerId); $subscription=$customer->subscriptions->retrieve($subscribeId); return $subscription; } public function cancelSubscription($customerId,$subscriptionId) { $customer=$this->getStripeCustomer($customerId); $subscription=$customer->subscriptions->retrieve($subscriptionId)->cancel(array('at_period_end'=>true)); return $subscription; } public function getFreeDays($company) { $user=$company->getUserCreated(); $now= new \DateTime(); $finishRegisterDate=$user->getFinishedRegister(); if($finishRegisterDate == null) return 15; $endLineDate=clone $finishRegisterDate; $freemiumDays=$this->container->getParameter('freemium_num_days'); $endLineDate->modify("+$freemiumDays days"); if($endLineDate->getTimeStamp() > $now->getTimeStamp()) return intval($now->diff($endLineDate)->format("%a")); else return 0; } public function hasPermission($company) { $user=$company->getUserCreated(); if(!$company->getRegistrationFinished()) return true; if($user->getSubscriptionId()!= null){ $subscription=$this->getSubscribeInformation($user->getStripeCustomerId(),$user->getSubscriptionId()); if($subscription->status=="active") return true; }else{ $infinitySubs = $user->getCompany()->getInfinitySubscription(); if($infinitySubs) return true; if($this->getFreeDays($company) > 0) return true; } return false; } public function createCharge($customer,$ammount,$currency) { $charge = \Stripe_Charge::create(array( 'customer' => $customer->id, 'amount' => $ammount, 'currency' => $currency )); return $charge; } }
Para terminar quería hablar de los webhooks. Estos webhooks son notificaciones de eventos que ocurren en nuestra cuenta de Stripe y a los que podemos suscribirnos. Empezar a recibir estos webhooks es tan sencillo como exponer una url que reciba estas llamadas y proporcionar dicha url a stripe. Estos eventos son muy útiles para notificar al usuario de las acciones y podemos escuchar una larga lista de eventos.
En el ejemplo que muestro a continuación nos subscribimos al evento “customer.subscription.deleted” de tal manera que cuando una subscripción sea borrada por ejemplo por que se ha caducado el pago, al usuario directamente se le elimina la referencia a dicha subscripción en nuestra aplicación.
public function webHooksAction(Request $request) { $em=$this->getDoctrine()->getManager(); $repository=$em->getRepository('EjemploBundle:User'); $stripe=$this->get('stripe'); $response = new Response(); if($request->getMethod()=="POST"){ $body = @file_get_contents('php://input'); $event_json = json_decode($body); $event=$stripe->getStripeEvent($event_json->id); //$event=true; if($event){ // event subscription deleted if($event_json->type=="customer.subscription.deleted"){ $data=$event_json->data; $object=$data->object; $customerId=$object->customer; $user=$repository->findOneByStripeCustomerId($customerId); if($user){ $user->setSubscriptionId(null); $em->persist($user); $em->flush(); $response->setContent("OK"); return $response; } } $response->setStatusCode(200); $response->setContent("Event not managed"); return $response; } } $response->setStatusCode(404); return $response; }
Un punto a destacar es que para los eventos que queramos ignorar debemos devolver un código de respuesta como que hemos recibido el evento, ya que stripe seguirá enviando estos eventos hasta que tu des un OK. Tuve la experiencia personal de no devolver estos OK y Stripe se puso en contacto con nosotros tras varios días para comunicar que arregláramos el problema o desactivarían las llamadas por la carga que esto implica a su sistema de reenviar todos estos eventos.
Espero que haya sido útil este post y que os animéis a emplear Stripe en vuestros desarrollos