APIs e Microsserviços

1 fev, 2012

Utilizando PayPal Frete Fácil com Ajax e PHP

Publicidade

Para quem ainda não conhece o PayPal Frete Fácil, é um serviço que oferece um desconto de até 30% no frete dos Correios para vendas feitas por PayPal. O site do serviço pode ser acessado em: https://ff.paypal-brasil.com.br/FretesPayPal/. É de praxe, em uma loja online, calcular o frete para a entrega dos produtos comprados pelo cliente. Normalmente, esse cálculo é feito através de um formulário na página do carrinho de compras, onde o cliente informa o CEP de destino e a aplicação faz o cálculo utilizando os pesos e dimensões dos produtos que estão no carrinho de compras do cliente. O PayPal Frete Fácil utiliza o mesmo sistema de cálculo de frete dos Correios, sendo assim, as regras referentes aos pacotes das encomendas também se aplicam ao PayPal Frete Fácil. São elas:

  1. O peso deve ser informado em Kilogramas. Caso a embalagem tenha menos do que 1 Kilograma, 300 gramas por exemplo, o valor informado deverá ser 0.3.
  2. Altura, Largura e Comprimento deverão ser informados em centímetros.
  3. O comprimento da embalagem não pode ser maior do que 90 centímetros.
  4. O comprimento da embalagem não pode ser menor do que 16 centímetros.
  5. A largura da embalagem não pode ser maior do que 90 centímetros.
  6. A largura da embalagem não pode ser menor do que o 5 centímetros.
  7. A largura da embalagem não pode ser menor do que o 11 centímetros quando o comprimento for menor do que 25 centímetros.
  8. A altura da embalagem não pode ser maior do que 90 centímetros.
  9. A altura da embalagem não pode ser menor do que 2 centímetros.
  10. A altura da embalagem não pode ser maior do que o comprimento.
  11. A soma da altura, largura e comprimento da embalagem não pode ser maior do que 160 centímetros.

Conhecidas as regras dos Correios, vamos utilizar o seguinte wrapper PHP para a integração com o PayPal Frete Fácil:

  <?php
/**
 * Wrapper para consumo do webservice PayPal Frete Fácil.
 */
class PayPalFreteFacil {
    /**
     * Caminho para o webservice do PayPal Frete Fácil
     */
    const ADDRESS = 'https://ff.paypal-brasil.com.br/FretesPayPalWS/WSFretesPayPal';
    
    /**
     * @var            stdClass
     * @property    integer $altura Altura da embalagem
     * @property    string $cepDestino CEP de destino
     * @property    string $cepOrigem CEP de origem
     * @property    integer $largura Largura da embalagem
     * @property    string $peso Peso da embalagem
     * @property    integer $profundidade Profundidade da embalagem
     */
    private $request;
    
    /**
     * O PayPal Frete Fácil é um serviço onde você tem desconto a partir de 30%
     * no valor do frete, em relação ao valor de balcão do SEDEX dos Correios,
     * para vendas feitas pelo PayPal.
     * @param    string $cepOrigem CEP de Origem.
     * @param    string $cepDestino CEP de Destino.
     */
    public function __construct( $cepOrigem = null , $cepDestino = null ) {
        $this->request = new stdClass();
        $this->request->altura = 0;
        $this->request->largura = 0;
        $this->request->peso = null;
        $this->request->profundidade = 0;
        
        $this->setCepDestino( $cepDestino );
        $this->setCepOrigem( $cepOrigem );
    }
    
    /**
     * Calcula o valor do frete para os dados informados.
     * @return    float O valor do frete para os dados informados.
     * @throws    RuntimeException Caso não tenha sido possável consumir
     *             o serviço de cálculo de frete.
     */
    public function getPreco() {
        try {
            return $this->getSoapClient()->getPreco( $this->request )->{'return'};
        } catch ( Exception $e ) {
            throw new RuntimeException( 'Falha ao consumir o webservice' , $e->getCode() , $e );
        }
    }
    
    /**
     * @return    SoapClient
     */
    protected function getSoapClient() {
        $client = new SoapClient( PayPalFreteFacil::ADDRESS . '?wsdl' , array(
            'trace'            => true,
            'exceptions'    => true,
            'style'            => SOAP_DOCUMENT,
            'use'            => SOAP_LITERAL,
            'soap_version'    => SOAP_1_1,
            'location'        => PayPalFreteFacil::ADDRESS,
            'encoding'        => 'UTF-8'
        ) );
        
        return $client;
    }

    /**
     * Define a altura da embalagem.
     * @param    integer $altura
     */
    public function setAltura( $altura ) {
        $this->request->altura = (int) $altura;
    }

    /**
     * Define o CEP de destino.
     * @param    string $cepDestino
     */
    public function setCepDestino( $cepDestino ) {
        $this->request->cepDestino = $cepDestino;
    }

    /**
     * Define o CEP de origem.
     * @param    string $cepOrigem
     */
    public function setCepOrigem( $cepOrigem ) {
        $this->request->cepOrigem = $cepOrigem;
    }

    /**
     * Define a largura da embalagem.
     * @param    integer $largura
     */
    public function setLargura( $largura ) {
        $this->request->largura = (int) $largura;
    }

    /**
     * Define o peso da embalagem.
     * @param    string $peso
     */
    public function setPeso( $peso ) {
        $this->request->peso = $peso;
    }

    /**
     * Define a profundidade da embalagem.
     * @param    integer $profundidade
     */
    public function setProfundidade( $profundidade ) {
        $this->request->profundidade = (int) $profundidade;
    }
}

O código do wrapper pode ser baixado diretamente do repositório do PayPal X Brasil no github: https://github.com/paypalxbrasil/paypal-frete-facil-php. Para demonstrar o cálculo em um carrinho de compras, vamos montar uma lista de produtos estáticas apenas para fins ilustrativos:

 <!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title>Exemplo de código com PayPal Frete Fácil</title>
        <script src="js/jquery.js"></script>
        <script src="js/exemplo.js"></script>
        <link rel="stylesheet" type="text/css" href="css/exemplo.css" />
    </head>
    <body>
        <div class="max-width">
            <h1>PayPal Frete Fácil</h1>
            <h2>Carrinho de compras</h2>
            <table>
                <caption>Itens do carrinho</caption>
                <thead>
                    <tr>
                        <th width="40%">Produto</th>
                        <th width="20%">Quantidade</th>
                        <th width="20%">Valor Unitário</th>
                        <th width="20%">Valor Total</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td width="40%" class="left">Produto de teste</td>
                        <td width="20%"><input type="text" name="qtd[1]" value="1" /></td>
                        <td width="20%" class="right price">RINSERT:CONTENT:ENDnbsp;<span>127.88</span></td>
                        <td width="20%" class="right total">RINSERT:CONTENT:ENDnbsp;<span>127.88</span></td>
                    </tr>
                    <tr>
                        <td width="40%" class="left">Outro Produto</td>
                        <td width="20%"><input type="text" name="qtd[2]" value="1" /></td>
                        <td width="20%" class="right price">RINSERT:CONTENT:ENDnbsp;<span>123.45</span></td>
                        <td width="20%" class="right total">RINSERT:CONTENT:ENDnbsp;<span>123.45</span></td>
                    </tr>
                </tbody>
                <tfoot>
                    <tr class="frete">
                        <td colspan="3" width="80%" class="left">
                            <label for="cep">CEP:</label>
                            <input id="cep" name="cep" />
                            <input id="calccep" type="button" value="Calcular" />
                        </td>
                        <td width="20%" class="right total">RINSERT:CONTENT:ENDnbsp;<span>0.00</span></td>
                    </tr>
                    <tr>
                        <td colspan="3" class="right"><strong>Total Geral</strong></td>
                        <td class="right cart-total">RINSERT:CONTENT:ENDnbsp;<span>251.33</span></td>
                    </tr>
                </tfoot>    
            </table>
        </div>
    </body>
</html>

Para formatar essa tela do carrinho, a seguinte folha de estilo foi utilizada:

 html,* {
    margin        : 0px;
    padding        : 0px;
    font-family    : Helvetica; 
}

.max-width {
    display        : block;
    width        : 900px;
    margin        : 0px auto;    
}

.right {
    text-align    : right;
    padding-right    : 10px;
}

.left {
    text-align    : left;
    padding-left    : 10px;
}

table {
    border-collapse: collapse;
    border-spacing: 0;
    margin-top        : 20px;
}

table caption {
    background    : #333;
    color        : #FFF;
    border-radius: 5px 5px 0px 0px;
    padding            : 10px 0px;
    font-weight: bold;
}

table thead {
    background    : #DADADA;
    color        : #333;
    border-bottom    : 1px dashed #333;
}

table thead tr th {
    padding        : 5px 0px;
    font-weight    : normal;
}

table tbody tr {
    height        : 40px;
    line-height    : 40px;
}
table tbody tr:hover {
    background    : #FDFDFD;
}

table tfoot {
    background    : #DADADA;
    border-bottom    : 4px solid #333;
    border-top    : 1px dashed #333;
}

table tfoot tr {
    height        : 40px;
}

tr.frete {
    background    : #EEE;
}

O código Javascript que fará a requisição faz uso do framework jQuery segue abaixo:

 /**
 * Calcula o total do carrinho 
 */
function cartTotal() {
    var total = 0;
    
    $('td.total span').each(function(){
        total += parseFloat($(this).text());
    });
    
    total = Math.round( total * 100 ) / 100;
    
    $('td.cart-total span').text(total);
}

$(function(){
    /**
     * Observa por mudanças no campos de quantidade e recalcula
     * os preços totais de cada produto e o total do carrinho.
     */
    $('tbody tr td input').change(function(){
        try {
            var tr = $(this).parent().parent();
            var val = parseInt( $(this).val() );
            var total = parseFloat(tr.find('td.price span').text())*val;
            
            tr.find('td.total span').html(total);
            $('tr.frete td span').text( '0.00' );
            
            cartTotal();
        } catch ( e ){
            $(this).val(1);
        }
    });
    
    /**
     * Observa o clique no botão de cálculo de frete e atualiza o total
     * do carrinho com o valor do frete.
     */
    $('#calccep').click(function(){
        var cep = $('#cep').val();
        var items = 0;
        var button = $(this);
        
        /**
         * Verifica o total de itens do carrinho
         */
        $('tbody tr td input').each(function(){
            items += parseInt($(this).val());
        });
        
        /**
         * Faz a requisição ajax ao servidor para o cálculo do frete
         */
        $.ajax( {
            url        : 'frete.php',
            type    : 'post',
            data    : { cep : cep , items : items },
            dataType: 'json',
            success    : function( resp ) {
                if ( resp.success ) {
                    $('tr.frete td span').text( resp.frete );
                    
                    cartTotal();
                } else {
                    alert( resp.message );
                }
            }
        } );
    });
});

A requisição é feita à:

 <?php
require_once '../lib/PayPalFreteFacil.php';

$altura = 2;
$largura = 15;
$comprimento = 30;
$peso = 1;

$items = isset( $_POST[ 'items' ] ) ? (int) $_POST[ 'items' ] : 1;

if ( $items < 1 ) {
    $items = 0;
}

$std = new stdClass();
$std->message = '';
$std->success = false;
$std->frete = 0;

if ( isset( $_POST[ 'cep' ] ) && preg_match( '/^\d{4,5}-?\d{3}$/', $_POST[ 'cep' ] ) ) {
    $ffPayPal = new PayPalFreteFacil();
    $ffPayPal->setCepOrigem( '04094-050' );
    $ffPayPal->setCepDestino( $_POST[ 'cep' ] );
    $ffPayPal->setAltura( $altura * $items );
    $ffPayPal->setLargura( $largura );
    $ffPayPal->setProfundidade( $comprimento );
    $ffPayPal->setPeso( $peso * $items );
    
    $std->frete = $ffPayPal->getPreco();
    
    if ( $std->frete <= 0 ) {
        $std->message = 'Falha ao calcular o frete';
    } else {
        $std->success = true;
    }
} else {
    $std->message = 'CEP inválido';
}

echo json_encode( $std );

E pronto, ao modificar a quantidade nos itens do carrinho o valor do frete é zerado. Sempre que o cliente clicar no botão calcular frete, a quantidade de itens e o CEP de destino são enviados ao servidor. Quando esses dados chegarem ao servidor, uma requisição ao serviço PayPal Frete Fácil é feita, informando os dados para o cálculo. Caso tudo ocorra bem, o valor do frete é devolvido e informado ao cliente; caso contrário, uma mensagem de erro será exibida ao cliente informando do ocorrido.