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:
- 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.
- Altura, Largura e Comprimento deverão ser informados em centímetros.
- O comprimento da embalagem não pode ser maior do que 90 centímetros.
- O comprimento da embalagem não pode ser menor do que 16 centímetros.
- A largura da embalagem não pode ser maior do que 90 centímetros.
- A largura da embalagem não pode ser menor do que o 5 centímetros.
- 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.
- A altura da embalagem não pode ser maior do que 90 centímetros.
- A altura da embalagem não pode ser menor do que 2 centímetros.
- A altura da embalagem não pode ser maior do que o comprimento.
- 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.