Como modularize MI plataforma de pagos para venta de cursos/libros con Stripe/Paypal en web y mobile

Te comento cómo logré modularizar mi plataforma de pagos en Laravel para que sea lo más facilmente escalable posible.

Te quería mostrar la estructura que yo tengo aquí implementada para manejar los pagos que en este caso al menos a la fecha tengo PayPal o tengo stripe tanto para comprar ya sea un curso o para comprar que es esta que estás viendo por acá o para comprar un libro Fíjate que es exactamente el mismo plugin tal cual puedes ver entonces esto lo considero importante ya que esto yo creo que puede ser de las cosas más complicadas que se puede implementar en nuestra aplicación web y sobre todo que se nos puede salir fácilmente de control ya que realidad es siempre crear implementaciones que sean fácilmente entre comillas escalables o al menos que sea lo más fácil de escalar posibles y que sean bastante entendibles y que cuando las queramos cambiar no sean tan propensas errores pero en este caso para los pagos como te digo siempre es un poco complicado sobre todo cuando vamos a producción que tuviéramos que trabajar con ya dinero real Entonces la cosa se puede liar bastante entonces otra vez es por eso la importancia de siempre crear una estructura bastante modular y que pueda ser fácilmente escalada tal cual yo hice en este caso cuando implemente la de PayPal luego decidí agregar la de stripe 

Dos metodos principales a modularizar Petición a la API

Entonces toda la final parte de una de un mismo núcleo ya que en un pago tenemos que procesar dos cosas por una parte hacer todo el proceso que nos solicite aquí la plataforma por ejemplo PayPal o Stripe que tengamos que implementar a nivel de nuestra aplicación en tiendas instalar el plugin crear el objeto llamar algún método en la API de stripe PayPal etcétera etcétera etcétera y por otra parte una vez obtenida la respuesta satisfactoria de esta Api hacer nosotros el proceso por lo tanto por una parte tenemos un método que es independiente a la estructura o al método de pago y por otra parte tenemos un método en común Entonces desde el inicio siempre tenemos que empezar a modularizar todo esto para que no nos coma vivos 

Mostrando mi implementación

Así que te voy a mostrar un poquito mi estructura Que obviamente es mejorable pero yo voy poco a poco en todo esto bien a la final:

protected function sendRequestAPI(string $orderId, string $type)
{
    if ($type == 'stripe-session-id') {
        // stripe app web
        $this->responseAPI = $this->checkPayedSession(request("stripe_session_id"));
        if ($this->responseAPI->payment_status == 'paid') {
            $this->status = 'COMPLETED';
            $this->idAPI = request("stripe_session_id");
            $this->payment = 'stripe';
            $this->price = intdiv($this->responseAPI->amount_total, 100);
        } else {
            // respuesta error stripe excepcion
            return $this->errorResponse("", 202, json_encode($this->responseAPI));
        }
    } else if ($type == 'stripe-payment-intent') {
        // stripe app flutter
        $this->responseAPI = $this->checkPaymentIntentById($orderId);
        if ($this->responseAPI->status == 'succeeded') {
            $this->status = 'COMPLETED';
            $this->idAPI = $orderId;
            $this->payment = 'stripe';
            $this->price = intdiv($this->responseAPI->amount, 100);
        } else {
            // respuesta error stripe excepcion
            return $this->errorResponse("", 202, json_encode($this->responseAPI));
        }
    } ***
 
}

Recomendación 1: Usa una relación Polimorfica 

Como te digo tengo dos tipos de entidades uno es libros y el otro es el de tutoriales aquí ya me hago una crítica ya que por ejemplo para libros sí lo hice libros es de tipo fileable es decir es una es de tipo polimórfica cosa que tuve que implementar desde el día uno no sé si ha seguido un poco el desarrollo de mi web conmigo pero yo comencé fue vendiendo los cursos y a posterior a fechas recientes yo agregué la parte de ventas de libros por lo tanto fue un cambio posterior así que esto lo que implicaba era que obviamente es una estructura distinta y por lo tanto tiene un manejo distinto ya que la información o la entidad de cursos no es la misma que la de libros cosa que sí hubieran podido ser si desde el día uno hubiera creado cosa que lo puedo hacer pero tengo que hacer esos cambios los tutoriales que fueran también de un tipo polimórfico y luego fácilmente entre comillas hubiera podido crear la parte de polimórficos es decir una una relación nueva para simplemente Boot entonces esa es la primera recomendación para haa lo que sea que quieras vender intenta crear por ahí y manejar desde ese día uno un un tipo que sea de tipo polimórfico y es por eso que aquí yo tengo dos archivos porque si no lo hubiera podido manejar perfectamente desde un solo controlador y simplemente mediante un condicional preguntar si es libros y registrar o hacer el proceso adicional para los libros y hacer o si es tutoriales hacer el proceso adicional para los tutoriales ya de igual manera te voy a mostrar aquí un poquito la implementación entonces 

Métodos de pago en 2 plataformas distintas = 4 Plataformas de pago...

A la final cuál es también otra problemática que yo tengo que esta sí no la voy a abrir pero yo también tengo una aplicación móvil Es decir para Android a la fecha y por lo tanto también ofrezco los mismos métodos de pago pero cuál es el problema aquí que los mismos cambios las implementaciones cambian por por una parte el de PayPal si ha seguido mi curso del árabe o el libro y tes de saber que por una parte hacemos una implementación en el cliente es decir creamos la orden que viene siendo como la intención de pago y por otra parte se pasa ese objeto al servidor para luego ser procesado es decir se hacen dos pasos pero el de Flutter que es la aplicación móvil funciona en un solo paso ya que desde el mismo cliente se realiza tanto la creación de la orden como el procesado de la misma por lo tanto no hace falta pasar por el servidor y creo que tiene todo todo el sentido del mundo ya que la idea de una aplicación móvil es que pueda hacer el pago sin necesidad de estar conectándose a un apid un tercero que en este caso sería mi aplicación de Academia web y de stripe más o menos lo mismo por lo tanto ya serían cuatro tipos de métodos de pago simplemente para una misma aplicación tal cual puedes ver imagínate

La peor implementación, 1 controlador para cada plataforma

Si quieres agregar otra o si luego quieres escalar se vuelve una pesadilla entonces por ahí puedes ver que algunas algunos problemas que pudiéramos tener en la implementación antes yo maneja un controlador para lo que era la aplicación web y otro controlador para la aplicación móvil Pero la final todo va de lo mismo Ya que queremos es hacer el proceso que pida la ap ya sea de PayPal o stripe y por otra parte luego ha el proceso que nosotros requiramos para registrar el recurso y entiendes el libro o el tutorial Pero puedes ver que ya hay muchas cositas Por una parte tenemos la implementaciones para los tutoriales Por otra parte para los libros por otra parte si estamos empleando aquí en el web PayPal o stripe y por parte si estás empleando eh o estás comparando desde la aplicación móvil Entonces ya son como ocho escenarios distintos y por lo tanto puede liarse bastante entonces Aquí empieza a funcionar.

Modularizar la app, traza, ID, monto, status y tipo

Aquí otra vez lo que tenemos que pensar es qué demonios podemos modularizar para que todo sea un único pago entonces voy a comenzar mostrándote un poquito aquí el método que se encarga de hacer esta discriminación qué es lo que necesitamos registrar Por una parte sería bueno tener toda la respuesta de la Api es decir eso siempre cuando se hace el pago siempre nos arroja un objeto inmenso que tiene información a veces nos coloca el email en el caso de PayPal de la persona que pagó también la fecha también el estatus si fue completado eh un montón de información esa trasa siempre es bueno guardarla por si ocurre algún problema de disputas Entonces ya tú tienes toda esa información ahí aunque realmente no se va a emplear lo conviertes a Jason y s feliz qué otra cosa vamos a necesitar el identificador obviamente es decir si a ti te van a hacer un pago por PayPal y ponte tú que ocurrir algún problema en tu aplicación entonces lo recomendado es que lo verifiques tú de manera manual y Para eso tiene la persona que pasar el identificador ya sea de PayPal o stripe y tú lo verificas de manera manual desde tu módulo o lo que sea y ahí lo registras de manera directa lo otro es el tipo Obviamente si está pagando por PayPal se está pagando por banco se está pagando por strike se está pagando por no sé Paloma mensajera lo que sea ahí colocas el tipo perfectamente puede ser un tipo numerado pero en este caso yo lo manej de esta forma simplemente con un token pero seguramente más adelante lo migo como te digo esta estructura todavía es mejorable pero me un poco la idea de eso de hablar en lo demás es el estatus como te comentaba que esto también lo tenemos que unificar ya que por ejemplo en el caso de PayPal y ex obviamente son distintos en el de PayPal es complete creo que era el estatus entonces nosotros lo tenemos que unificar para que mediante una misma implementación podamos registrar distintos métodos de pagos y es por eso que tengo este método intermedio ya que fíjate que no es final porque está protegido entonces qué es lo que hace este método:

public function processTutorial(Tutorial $tutorial, string $orderId, string $type = 'paypal-order-id')
{
    $user = $this->getUser();

    if ($user == null) {
        return $this->errorResponse("", 202, 'Usuario no encontrado, no se hizo ningun cargo a tu cuenta');
    }

    //*** curso pago
    $this->sendRequestAPI($orderId, $type);

    //*** registra el recurso si la orden esta ok
    if ($this->status == 'COMPLETED') {
        // respuesta exito paypal
        return $this->registerTutorialToUser(
            ***
        );
    } else {
        // respuesta error paypal excepcion
        return $this->errorResponse("", 202, "Ha ocurrido un problema con su orden, el estatus es " . $this->status . " y el ID es " . $this->idAPI . " La respuesta es: " . json_encode($this->responseAPI));
    }
}

La orden es decir el identificador perfecto amente hubiera podido colocar ID el ses ID lo que serí en strike o el orden ID es lo que sería en PayPal yo le coloqué orden ID y me da igual y por aquí un tipo para que me indique qu demonios es el que le está llamando Entonces ya aquí puedes ver que empecé a discriminar un poco Y a partir de aquí y siguiendo un poco con el video que también te hablaba sobre la importancia de emplear propiedades de clases a nivel de los controladores ya que a la final se nos puede olvidar que los controladores son clases y con esto podemos gozar de todas las características que tenemos de las clases como en este caso es son las propiedades simplemente lleno las propiedades que van a ser comunes en base a lo explicado anteriormente el precio también es otro importante tal cual puedes ver por acá y como quien dce lo creamos una estructura genérica independientemente de qué demonios estemos empleando Entonces por ejemplo en el caso de strip tiene la Manía de que si le pasas un es realmente 100:

$this->price = intdiv($this->responseAPI->amount_total, 100);

Entonces aquí lo tenemos que dividir entre 100 Porque yo lo estoy lo estoy guardando en el monto real es decir si es un dólar que cuesta el producto yo guardo eso un uno entonces por eso aquí lo divido entre 100 por lo demás aquí le coloco un estatus común aquí obtengo el identificador Eso depende un poco Cómo funciona el app ya que fíjate que por ejemplo el identificador en el caso de strip con el session ID funciona de esta forma pero en el caso de strike Up floter lo tengo aquí directamente también lo puedo pasar aquí directamente que es lo que tengo acá y en el caso de PayPal lo tengo es desde la respuesta me parece o desde o se la paso para ver Ah no también establecida de esta forma pero aquí es lo importante o sea tú ves donde demonios la sacas ya que por más que sea existen muchas formas de obtener esa información y aquí estamos unificando para trabajar con patrones comunes que en este caso es lo que nosotros vamos a necesitar dando un pequeño resumen otra vez qué es lo que nosotros vamos a requerir por ejemplo aquí estoy viendo que tengo un error ya que esto Debería ser así ya que lo estoy aquí igualando entonces yo voy a necesitar la traza completa el estatus para saber si se pagó o no el precio y también el ID el estatus pu pudieras precindir de él pero yo lo manejo es más que todo de manera interna para si ocurrió algún error entonces mostrar una página de error pero lo que vamos a registrar en la base de datos sería el precio el ID y también la traza Entonces como te indicaba:

Método intermedio para procesar TODAS las peticiones de compra y modularizar la app mediante propiedades

Yo tengo cuatro plataformas a la fecha y todas funcionan de manera distintas estri y PayPal pero una para móvil y otra para web por lo tanto ya son dos y lo mismo con stripe y aquí lo que hago es establecer las propiedades para que tengan independientemente del origen tengan el valor Así que este método intermedio se llama es directamente desde los controladores que otra vez es solamente uno para el la aplicación web y para la aplicación en Flutter qué es lo que hacemos acá obtener el usuario y aquí también tenemos otra novela porque con laravel tenemos dos formas sería mediante tokens que yo la tengo aquí también habilitada para en el caso de la aplicación móvil:

$user = $this->getUser();

Más adelante también lo voy a implementar para la aplicación web y mediante sesión Porque es una aplicación en larabel también web entonces independientemente de yo aquí verifico de dónde lo saco y aho tengo el usuario y si me devuelve un nulo Entonces tenemos problemas porque no tengo a quien registrar el recurso que esto también da problemas por ejemplo en la parte de PayPal por ejemplo con el que hace toda la operación desde cliente Porque si hac toda la operación desde el cliente es decir si tú no bloqueas el plugin de PayPal desde el cliente por un usuario no autenticado entonces obviamente no tengo a quien registrar el curso y bueno explotó la cosa pero bueno.

Así que espero que haya quedado un poco claro y esto te ayude Para futuras implementaciones de tus aplicaciones

- Andrés Cruz

In english

Andrés Cruz

Desarrollo con Laravel, Django, Flask, CodeIgniter, HTML5, CSS3, MySQL, JavaScript, Vue, Android, iOS, Flutter

Andrés Cruz En Udemy

Acepto recibir anuncios de interes sobre este Blog.