Laravel + PayPal - Parte 1: Criando projeto
Tags: laravel, paypal, tutorial, php
Published: 2020-03-09 16:58:29

Recentemente eu participei em um projeto da empresa júnior [InfoAlto](https://infoalto.com.br) na qual precisava de um módulo de pagamentos do  tipo assinatura, o cliente tinha preferência por utilizar o PayPal como gatway de pagamento e isso me trouxe algumas dificuldades. Foi o segundo projeto que fiz na qual tive contato com meios de pagamento (realizei um anteriormente que tinha um checkout porém utilizando o pagseguro) e eu não tinha noção de como funcionava os pagamentos recorrentes, além disso, uma documentação um pouco confusa entre versões da API do PayPal e um SDK para PHP desatualizado serviram como dificuldades para eu conseguir seguir com o projeto. Como eu não consegui encontrar muito conteúdo em português que me ensinasse a fazer o que precisava, decidi compartilhar o que consegui aprender. Por isso vou criar um pequeno projeto de venda/aluguel de filmes (algo similar ao youtube movies) e assinatura (como a netflix) e compartilhar com uma série de posts.

**Edit:** código foi atualizado para a versão 8 do laravel. Isso não implica em nenhuma mudança grave do que foi feito nessa parte

#Criando o projeto

O primeiro passo é criar o projeto com o comando **laravel new** com o nome do projeto, no meu caso vou chamar de laravel_paypal:
``` laravel new laravel_paypal ```

#Configurando banco de dados

Após o projeto ser criado, vamos configurar o nosso arquivo **.env** para fazer a conexão com o banco de dados
```
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=nome_banco
DB_USERNAME=nome_usuario
DB_PASSWORD=senha_usuario
```

Com isso, vamos criar 2 *migrations* novas para a nossa aplicação, sendo elas: movies e movie_user

####Migration **movies**

A tabela **movies** vai ser responsável por guardar as informações dos filmes disponíveis na plataforma

``` php artisan make:migration create_movies_table ```

O código da função up fica assim:
```
    public function up()
    {
        Schema::create('movies', function (Blueprint $table) {
            $table->id();
            $table->string("title");
            $table->string("image");
            $table->text("overview");
            $table->float("purchase_price")->nullable();
            $table->float("rental_price")->nullable();
            $table->timestamps();
        });
    }
```
- Campo *title*: é do tipo string (varchar no banco de dados) e guarda o título do filme;
- Campo *image*: é do tipo string (varchar no banco de dados) e guarda a URI da thumb do filme;
- Campo *overview*: é do tipo text e guarda o resumo ou sinopse do filme;
- Campo *purchase_price*: é tipo float e guarda o preço de compra do filme, se ele for nulo o filme não tem a opção de compra disponível;
- Campo *rental_price*: é do tipo float e guarda o preço de aluguel do filme, se ele for nulo o filme não tem a opção de aluguel disponível.

####Migration **movie_user**

A tabela **movie_user** vai ser responsável por armazenar o relacionamento entre um usuário e um filme

``` php artisan make:migration create_movie_user_table ```

O código da função up fica assim:

```
    public function up()
    {
        Schema::create('movie_user', function (Blueprint $table) {
            $table->id();
            $table->bigInteger("user_id");
            $table->bigInteger("movie_id");
            $table->dateTime("expires_at")->nullable();
            $table->dateTime("acquired_in");
        });
    }
```
- Campo *user_id*: é do tipo big int e guarda o ID do usuário;
- Campo *movie_id*: é do tipo big int e guarda o ID do filme;
- Campo *expires_at*: é do tipo datetime e guarda a data de expiração do aluguel do filme, se for nulo indica que o usuário fez a compra do filme;
- Campo *acquired_in*: é tipo datetime e guarda a data que o usuário adquiriu aquele filme, sendo aluguel ou compra.

####Migration **user**

Por padrão o Laravel já disponibiliza uma migration que cria a tabela *users*, por isso nós não precisamos criar-lá.


Agora que já temos nossas *migrations* é só rodar com o comando:
``` php artisan migrate ```

#Criando Models e Seeder

Com as tabelas do banco criado está na hora de criarmos e configurarmos as Models e começar a popular o banco de dados

###Model Movie

``` php artisan make:model Movie ```

E o código da model fica assim:
```
class Movie extends Model
{
    protected $fillable = [
        "title", "image", "overview", "purchase_price", "rental_price"
    ];

    public function users() {
        $this->belongsToMany("App\User");
    }
}
```

- No atributo *fillable* colocamos todos os campos da tabela *movies* que é permitido inserir e fazer alterações;
- Na função *users()* criamos um relacionamento N:N com a Model User

###Model User

Por padrão a model User já vem na criação do projeto Laravel, por isso a unica alteração que vamos fazer é criar o relacionamento N:N com a Model Movie, adicionando a seguinte função na model:

```
    public function movies() {
        $this->belongToMany("App\Movie");
    }
```

> Lembrando que por padrão, o Laravel irá utilizar como tabela intermediária para esse relacionamento a tabela que criamos com a migration **movie_user**


###Populando o banco de dados

Para iniciar com a nossa aplicação, vamos popular a tabela **movies** com alguns filmes, para isso precisamos primeiro criar um *seeder* com o comando:
``` php artisan make:seeder MoviesTableSeeder ```

O código desse *seeder* fica assim:

```
<?php

use Illuminate\Database\Seeder;
use App\Movie;

class MoviesTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        Movie::insert([
            [
                "title" => "Homem de Ferro",
                "image" => "https://m.media-amazon.com/images/M/MV5BMTczNTI2ODUwOF5BMl5BanBnXkFtZTcwMTU0NTIzMw@@._V1_UX182_CR0,0,182,268_AL_.jpg",
                "overview" => "Depois de ser mantido em cativeiro em uma caverna afegã, o engenheiro bilionário Tony Stark cria uma armadura única e armada para combater o mal.",
                "purchase_price" => 12.99,
                "rental_price" => 40.99
            ],
            [
                "title" => "O Incrível Hulk",
                "image" => "https://m.media-amazon.com/images/M/MV5BMTUyNzk3MjA1OF5BMl5BanBnXkFtZTcwMTE1Njg2MQ@@._V1_UX182_CR0,0,182,268_AL_.jpg",
                "overview" => "Bruce Banner, um cientista em fuga do governo dos EUA, deve encontrar uma cura para o monstro em que se transforma, sempre que perde a paciência.",
                "purchase_price" => 12.99,
                "rental_price" => 40.99
            ],
            [
                "title" => "Homem de Ferro 2",
                "image" => "https://m.media-amazon.com/images/M/MV5BMTM0MDgwNjMyMl5BMl5BanBnXkFtZTcwNTg3NzAzMw@@._V1_UX182_CR0,0,182,268_AL_.jpg",
                "overview" => "Com o mundo agora ciente de sua identidade como Homem de Ferro, Tony Stark deve enfrentar tanto sua saúde em declínio quanto um homem louco e vingativo, ligado ao legado de seu pai.",
                "purchase_price" => 12.99,
                "rental_price" => 40.99
            ],
            [
                "title" => "Thor",
                "image" => "https://m.media-amazon.com/images/M/MV5BOGE4NzU1YTAtNzA3Mi00ZTA2LTg2YmYtMDJmMThiMjlkYjg2XkEyXkFqcGdeQXVyNTgzMDMzMTg@._V1_UX182_CR0,0,182,268_AL_.jpg",
                "overview" => "O poderoso, mas arrogante deus Thor, é expulso de Asgard para viver entre os humanos em Midgard (Terra), onde ele logo se torna um dos seus melhores defensores.",
                "purchase_price" => 12.99,
                "rental_price" => 40.99
            ],
            [
                "title" => "Capitão América: O Primeiro Vingador",
                "image" => "https://m.media-amazon.com/images/M/MV5BOGE4NzU1YTAtNzA3Mi00ZTA2LTg2YmYtMDJmMThiMjlkYjg2XkEyXkFqcGdeQXVyNTgzMDMzMTg@._V1_UX182_CR0,0,182,268_AL_.jpg",
                "overview" => "Steve Rogers, um soldado militar rejeitado, se transforma no Capitão América depois de tomar uma dose de um 'soro de super-soldado'. Mas ser Capitão América tem um preço, enquanto ele tenta derrubar um traficante de guerra e uma organização terrorista.",
                "purchase_price" => 12.99,
                "rental_price" => 40.99
            ],
            [
                "title" => "Os Vingadores",
                "image" => "https://m.media-amazon.com/images/M/MV5BNDYxNjQyMjAtNTdiOS00NGYwLWFmNTAtNThmYjU5ZGI2YTI1XkEyXkFqcGdeQXVyMTMxODk2OTU@._V1_UX182_CR0,0,182,268_AL_.jpg",
                "overview" => "Os heróis mais poderosos da Terra devem se unir e aprender a lutar em equipe, se quiserem impedir que o travesso Loki e seu exército alienígena escravizem a humanidade.",
                "purchase_price" => 12.99,
                "rental_price" => 40.99
            ],
            [
                "title" => "Homem de Ferro 3",
                "image" => "https://m.media-amazon.com/images/M/MV5BMjE5MzcyNjk1M15BMl5BanBnXkFtZTcwMjQ4MjcxOQ@@._V1_UY268_CR3,0,182,268_AL_.jpg",
                "overview" => "Quando o mundo de Tony Stark é dilacerado por um terrorista formidável chamado Mandarin, ele inicia uma odisseia de reconstrução e retribuição.",
                "purchase_price" => 12.99,
                "rental_price" => 40.99
            ],
            [
                "title" => "Thor: O Mundo Sombrio",
                "image" => "https://m.media-amazon.com/images/M/MV5BMTQyNzAwOTUxOF5BMl5BanBnXkFtZTcwMTE0OTc5OQ@@._V1_UY268_CR3,0,182,268_AL_.jpg",
                "overview" => "Quando os Elfos Negros tentam mergulhar o universo na escuridão, Thor deve embarcar em uma jornada perigosa e pessoal que o reunirá com a médica Jane Foster.",
                "purchase_price" => 12.99,
                "rental_price" => 40.99
            ],
            [
                "title" => "Capitão América: O Soldado Invernal",
                "image" => "https://m.media-amazon.com/images/M/MV5BMzA2NDkwODAwM15BMl5BanBnXkFtZTgwODk5MTgzMTE@._V1_UY268_CR1,0,182,268_AL_.jpg",
                "overview" => "Enquanto Steve Rogers luta para abraçar seu papel no mundo moderno, ele se une a um colega dos Vingadores e da S.H.I.E.L.D, a Viúva Negra, para combater uma nova ameaça da história: um assassino conhecido como Soldado Invernal.",
                "purchase_price" => 12.99,
                "rental_price" => 40.99
            ],
            [
                "title" => "Guardiões da Galáxia",
                "image" => "https://m.media-amazon.com/images/M/MV5BMTAwMjU5OTgxNjZeQTJeQWpwZ15BbWU4MDUxNDYxODEx._V1_UX182_CR0,0,182,268_AL_.jpg",
                "overview" => "Um grupo de criminosos intergaláticos deve se unir para parar um guerreiro fanático com planos de limpar o universo.",
                "purchase_price" => 12.99,
                "rental_price" => 40.99
            ],
            [
                "title" => "Vingadores: Era de Ultron",
                "image" => "https://m.media-amazon.com/images/M/MV5BMTM4OGJmNWMtOTM4Ni00NTE3LTg3MDItZmQxYjc4N2JhNmUxXkEyXkFqcGdeQXVyNTgzMDMzMTg@._V1_UX182_CR0,0,182,268_AL_.jpg",
                "overview" => "Quando Tony Stark e Bruce Banner tentam iniciar um programa de manutenção da paz adormecido chamado Ultron, as coisas saem terrivelmente erradas e cabe aos heróis mais poderosos da Terra impedir que o vilão Ultron decida executar seu plano terrível.",
                "purchase_price" => 12.99,
                "rental_price" => 40.99
            ]
        ]);
    }
}

```

E antes de rodar nosso *seeder* temos que inserir ele dentro do arquivo **DatabaseSeeder.php**:
```
class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     *
     * @return void
     */
    public function run()
    {
        $this->call(MoviesTableSeeder::class);
    }
}
```

Com isso só rodar o seguinte comando que nosso banco estará populado:
``` php artisan db:seed ```


###Criando a página inicial do sistema

Para criar a página inicial do sistema, vamos utilizar o módulo de login do Laravel. Se você está acostumando com a versão 5 do Laravel pode estranhar um pouco, pois antes bastava executar o comando **php artisan make:auth** e todo o módulo de login estava pronto. Porém na versão 6 isso começou a mudar, e agora na versão 7 é possível escolher entre 3 tipos de frameworks frontend para fazer a interface de sua aplicação. Como esse tutorial está mais focado em integrar o paypal com o laravel vou utlizar o bootstrap como framework frontend.

Primeiro, temos que instalar o pacote de interfaces do Laravel:
``` composer require laravel/ui ```

Após a instalação escolhemos a opção do bootstrap:
``` php artisan ui bootstrap --auth ```

Após esse comando, o laravel irá adicionar vários arquivos, como *migrations*, *controllers*, *views*, etc. Como ele cria uma nova *migration* é bom rodar o **php artisan migrate** novamente.

Para conseguir utilizar o pacote é necessário ter o npm instalado e executar o seguinte comando:
``` npm install && npm run dev ```

> Para baixar o node e npm só acessar esse [link](https://nodejs.org/en/)

###Configurando Controller E Views
Nesse inicio só iremos disponibilizar para view **home** todos os filmes salvos no banco de dados, com isso, o código do HomeController fica assim:

```
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Movie;

class HomeController extends Controller
{
    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('auth');
    }

    /**
     * Show the application dashboard.
     *
     * @return \Illuminate\Contracts\Support\Renderable
     */
    public function index()
    {
        $movies = Movie::all();
        return view('home', ['movies' => $movies]);
    }
}

```
 A *view* **home** fica assim:

```
@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-12">
            <div class="card">
                <div class="card-header">Filmes disponíveis</div>

                <div class="card-body">
                    @if (session('status'))
                        <div class="alert alert-success" role="alert">
                            {{ session('status') }}
                        </div>
                    @endif

                    <div class="row justify-content-around">
                    @foreach ($movies as $movie)
                        <div class="card" style="width: 15rem; margin-bottom: 10px;">
                            <img src="{{ $movie->image }}" class="card-img-top" alt="...">
                            <div class="card-body">
                                <h5 class="card-title">{{ $movie->title}}</h5>
                                <a href="#" class="card-link">Alugar</a>
                                <a href="#" class="card-link">Comprar</a>
                            </div>
                        </div>
                    @endforeach
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection
```

###Resultado final da parte 1
Com isso, temos como resultado final dessa primeira parte a seguinte tela:
![Imagem da parte 1!](https://gabrielm.dev/img/laravel-paypal/screencapture-paypal-p1.png)


###Referências
https://laravel.com/docs/7.x
https://laravel.com/docs/7.x/migrations
https://laravel.com/docs/7.x/eloquent
https://laravel.com/docs/7.x/eloquent-relationships#many-to-many
https://laravel.com/docs/7.x/seeding
https://laravel.com/docs/7.x/authentication
https://laravel.com/docs/7.x/frontend
https://laravel.com/docs/7.x/controllers
https://laravel.com/docs/7.x/views