Olá, pessoal.
Como temos percebido, com a popularização dos dispositivos moveis, surgem novos desafios e novas oportunidades; da mesma forma, surgem novas dúvidas e discussões.
Uma dessas discussões é sobre ter um site adaptado para dispositivos móveis ou ter uma aplicação para ser instalada nesses dispositivos.
De fato, existem linhas que defendem com veemência o desenvolvimento de uma aplicação móvel, outros, com convicção, garantem que é melhor ter o site, que deve ser adaptado.
Não vamos entrar no mérito dessa discussão, vamos apenas ilustrar as possibilidades que temos, os desafios e, ao final, teremos uma aplicação móvel utilizando a versão mobile do checkout PayPal.
O primeiro passo para começarmos o desenvolvimento de uma experiência de checkout é compreender, do ponto de vista do cliente, como isso funciona.
Qual seria o motivo pelo qual uma pessoa instalaria uma aplicação de uma loja em seu celular ou tablet?
Quem respondeu “Para comprar produtos da loja” está totalmente correto.
O único motivo que leva alguém a instalar uma aplicação de venda de produtos em um dispositivo móvel é para comprar facilmente um produto dessa loja.
Então já temos um ponto de partida:

Se o usuário quer navegar entre os produtos, precisamos oferecer a ele uma forma fácil de fazer isso, e precisamos, de fato, evitar qualquer coisa que impeça isso ou que ele precise ficar clicando em diversos lugares para chegar até a navegação de produtos.
Então, a primeira coisa que o cliente precisa ver em uma aplicação móvel são os produtos; e depois?
Muitas vezes, o cliente já conhece a loja e sabe exatamente o que ele quer; existem casos de o cliente não querer, sequer, passar por um carrinho de compras, ele abriu a aplicação, viu o produto e quer comprá-lo.

A loja não pode dificultar o desejo de “Comprar Agora” do cliente, não pode tentar forçá-lo a comprar mais, porque isso poderá fazê-lo não comprar de forma alguma.
Bom, temos agora um cliente navegando entre produtos e podendo comprar qualquer produto em qualquer momento. Mas e se o cliente quiser comprar mais de um item da loja? Obrigar o cliente a ir a um carrinho é um erro, mas outro erro maior ainda é não oferecer a possibilidade de o cliente comprar mais de um item. Temos que ter um carrinho também:

Ok, temos um carrinho. E agora ?
Não basta ter o carrinho, o cliente precisa conseguir visualizá-lo, revisar os itens e fazer o checkout:

Bom, como devem ter percebido, o “Comprar Agora” é um checkout rápido, mas oferecemos também a opção de fazer um checkout explicitamente clicando em um botão específico.
O fluxo de um checkout convencional seria parecido com a sequência abaixo:
- Cliente decide fazer checkout.
- Informa os dados de entrega.
- Escolhe uma forma de pagamento.
- Faz uma revisão do pedido e efetua o pagamento.
- Recebe uma confirmação do pedido
Com Express Checkout, o fluxo é um tanto parecido:
- Cliente decide fazer checkout.
- Se não estiver logado, faz login no PayPal.
- Faz uma revisão do pedido no PayPal.
- Confirma o pagamento.
- Recebe uma confirmação do pedido.
Agora que já compreendemos a experiência do usuário e o fluxo desde a intenção de compra até a concretização de uma venda, vamos ver como implementar isso.
No último artigo sobre Express Checkout, mostramos como fazer uma Integração com Express Checkout em um site convencional.
Utilizaremos exatamente a mesma estrutura do último artigo para fazer a integração em nossa aplicação mobile, ou seja, a aplicação que está no servidor que fará a comunicação com o PayPal. E o motivo é muito simples: quando um usuário está utilizando um dispositivo móvel, não temos como saber de onde ele o está usando. Ele pode estar, por exemplo, utilizando uma rede wireless em um shopping.
Se a rede desse shopping não for criptografada, um usuário mal intencionado pode lançar um ataque chamado “Man in the Middle” e interceptar a comunicação.
Se estivermos fazendo a comunicação diretamente entre o dispositivo móvel, e os dados forem interceptados, teremos um comprometimento total da segurança:
- Do cliente.
- Da sua loja.
- Do PayPal.
Isso porque, ao fazer a integração direta do dispositivo móvel, poderemos estar trafegando informações altamente sigilosas, como usuário, senha e assinatura da API PayPal em uma rede não criptografada. Isso causaria uma exposição totalmente inaceitável.
Como regra geral, jamais trafegue informações sigilosas de um dispositivo móvel, ao contrário, prefira enviar apenas informações não sigilosas para uma aplicação de back-end e, nessa aplicação, faça o tráfego das outras informações.
Bom, nesse caso, já temos então tudo o que precisamos para fazer a integração com o PayPal; vamos apenas dar uma melhorada naquele código:
checkout.php
<?php
require 'util.php';
$total = filter_input( INPUT_POST , 'total' , FILTER_VALIDATE_FLOAT );
$user = 'usuario.do.vendedor';
$pswd = 'senha';
$signature = 'ASSINATURA';
if ( is_float( $total ) ) {
$baseURL = 'http://dominio.com/';
$responseNvp = call( $user , $pswd , $signature,
'SetExpressCheckout' , array(
'PAYMENTREQUEST_0_AMT' => sprintf( '%.02f' , $total ),
'PAYMENTREQUEST_0_PAYMENTACTION' => 'Sale',
'PAYMENTREQUEST_0_CURRENCYCODE' => 'BRL',
'LOCALECODE' => 'pt_BR',
'RETURNURL' => $baseURL . '/retorno.php',
'CANCELURL' => $baseURL . '/cancelamento.php',
) , true
);
if ( isset( $responseNvp[ 'ACK' ] ) && $responseNvp[ 'ACK' ] == 'Success' ) {
showOutput( $responseNvp[ 'TOKEN' ] );
} else {
showOutput( 'OPZ' );
}
} else {
showOutput( 'OPZ' );
}
Como podem ver, diferentemente do último código, temos um arquivo novo, chamado util.php:
util.php
<?php
/**
* Exibe uma saída XML
* @param string $message Uma mensagem qualquer
*/
function showOutput( $message ) {
header( 'Content-Type: text/xml; charset=UTF-8' );
echo '<?xml version="1.0"?>' , PHP_EOL;
echo '<response>';
echo '<message>' , $message , '</message>';
echo '</response>';
}
/**
* Efetua uma chamada a uma operação do PayPal
* @param string $user Nome do usuário
* @param string $pwd Senha do usuário
* @param string $signature Assinatura de acesso
* @param string $operation Operação que será executada
* @param array $nvp Campos que serão enviados com a requisição
* @param boolean $close Indica se a conexão deverá ser fechada
* @return array Matriz associativa com os pares Nome=Valor retornados
*/
function call( $user , $pwd , $signature , $operation , array $nvp , $close = false ) {
static $curl = null;
$matches = array();
$response = array();
$nvp[ 'VERSION' ] = '64';
$nvp[ 'METHOD' ] = $operation;
$nvp[ 'PWD' ] = $pwd;
$nvp[ 'USER' ] = $user;
$nvp[ 'SIGNATURE' ] = $signature;
if ( !is_resource( $curl ) ) {
$curl = curl_init();
}
curl_setopt( $curl , CURLOPT_URL , 'https://api-3t.sandbox.paypal.com/nvp' );
curl_setopt( $curl , CURLOPT_SSL_VERIFYPEER , false );
curl_setopt( $curl , CURLOPT_RETURNTRANSFER , 1 );
curl_setopt( $curl , CURLOPT_POST , 1 );
curl_setopt( $curl , CURLOPT_POSTFIELDS , http_build_query( $nvp ) );
if ( preg_match_all( '/(?<name>[^\=]+)\=(?<value>[^&]+)&?/' , urldecode( curl_exec( $curl ) ) , $matches ) ) {
foreach ( $matches[ 'name' ] as $offset => $name ) {
$response[ $name ] = $matches[ 'value' ][ $offset ];
}
}
if ( $close ) {
curl_close( $curl );
}
return $response;
}
Com isso, nosso código do retorno também sofreu algumas modificações:
retorno.php
<?php
require 'util.php';
if ( isset( $_GET[ 'token' ] ) ) {
$token = $_GET[ 'token' ];
$user = 'usuario.do.vendedor';
$pswd = 'senha';
$signature = 'ASSINATURA';
$responseNvp = call( $user , $pswd , $signature,
'GetExpressCheckoutDetails' , array(
'TOKEN' => $token,
)
);
if ( isset( $responseNvp[ 'TOKEN' ] ) && isset( $responseNvp[ 'ACK' ] ) ) {
if ( $responseNvp[ 'TOKEN' ] == $token && $responseNvp[ 'ACK' ] == 'Success' ) {
$responseNvp = call( $user , $pswd , $signature,
'DoExpressCheckoutPayment' , array(
'TOKEN' => $token,
'PAYERID' => $responseNvp[ 'PAYERID' ],
'PAYMENTREQUEST_0_AMT' => $responseNvp[ 'PAYMENTREQUEST_0_AMT' ],
'PAYMENTREQUEST_0_CURRENCYCODE' => $responseNvp[ 'PAYMENTREQUEST_0_CURRENCYCODE' ],
'PAYMENTREQUEST_0_PAYMENTACTION' => 'Sale',
) , true
);
if ( isset( $responseNvp[ 'PAYMENTINFO_0_PAYMENTSTATUS' ] ) && $responseNvp[ 'PAYMENTINFO_0_PAYMENTSTATUS' ] == 'Completed' ) {
showOutput( 'SUCESSO' );
} else {
showOutput( 'OPZ' );
}
} else {
showOutput( 'OPZ' );
}
} else {
showOutput( 'OPZ' );
}
} else {
showOutput( 'OPZ' );
}
Para construir a aplicação mobile, vamos utilizar o Adobe AIR como plataforma. Abaixo, a lista de tudo o que precisaremos:
Vamos utilizar o Adobe AIR, pois, desde a versão 2.6, podemos escrever uma única aplicação que rode tanto no Android quanto no iOS.
Começaremos escrevendo a base da aplicação:
PayPal.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:ViewNavigatorApplication
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
firstView="com.paypal.example.views.Home"
splashScreenImage="@Embed('splash.png')">
<s:navigationContent>
<s:Button click="navigator.popToFirstView()" icon="@Embed('paypal-icon_48x48.png')" />
</s:navigationContent>
<s:actionContent>
<s:Button click="navigator.pushView(com.paypal.example.views.ShoppingCart)" icon="@Embed('cart.png')" />
</s:actionContent>
</s:ViewNavigatorApplication>
Nossa primeira View é a Home, onde exibiremos os produtos ao usuário:
Home.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:View
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
title="Produtos">
<fx:Script>
<![CDATA[
import spark.components.DataGroup;
import com.paypal.example.Application;
import com.paypal.example.Cart;
private var application :Application = Application.instance();
private var cart :Cart = application.cart();
private function buyNow() :void {
checkoutButton.enabled = false;
cart.addEventListener( "SUCCESS" , function() :void {
checkoutButton.enabled = true;
navigator.pushView( com.paypal.example.views.PayPalCheckout , cart.token );
cart.clean();
} );
cart.addEventListener( "OPZ" , function() :void {
checkoutButton.enabled = true;
} );
cart.buyNow( list.selectedItem )
}
private function scrollList() :void {
var index :int = list.selectedIndex;
if ( ( index != -1 ) && ( list.layout != null ) ) {
var target <img src="https://www.paypal-brasil.com.br/x/wp-includes/images/smilies/icon_biggrin.gif" alt=":D" class="wp-smiley"> ataGroup = list.dataGroup;
var spDelta:Point = target.layout.getScrollPositionDeltaToElement( index );
if ( ( spDelta != null ) && ( spDelta.x != 0 ) ) {
pth.valueBy = spDelta.x;
animation.play();
}
}
}
]]>
</fx:Script>
<fx:Declarations>
<mx:CurrencyFormatter
id="currencyFormatter"
currencySymbol="R$"
precision="2"
decimalSeparatorFrom=","
decimalSeparatorTo=","
useNegativeSign="true"
useThousandsSeparator="false"
alignSymbol="left" />
<s:Animate id="animation" target="{listLayout}" duration="100">
<s:motionPaths>
<s:SimpleMotionPath id="pth" property="horizontalScrollPosition" />
</s:motionPaths>
</s:Animate>
<fx:XML id="products" source="http://189.90.143.178/pp/products.xml" />
</fx:Declarations>
<s:VGroup
width="100%"
height="100%"
horizontalAlign="center">
<s:List
id="list"
itemRenderer="com.paypal.example.renderers.ProductRenderer"
top="0"
left="0"
width="100%"
height="400"
dragMoveEnabled="true"
borderVisible="false"
selectedIndex="0"
enabled="true"
change="scrollList()"
contentBackgroundColor="0xFFFFFF">
<s:layout>
<s:HorizontalLayout
id="listLayout"
gap="0"
paddingLeft="0"
paddingRight="0"
paddingTop="0"
paddingBottom="0"
verticalAlign="top" />
</s:layout>
<s:dataProvider>
<s:XMLListCollection source="{products.children()}" />
</s:dataProvider>
</s:List>
<s:Label
text="{list.selectedItem.name}"
fontSize="30"
fontWeight="bold"
color="0x013262" />
<s:HGroup
width="100%"
paddingTop="10"
horizontalAlign="center">
<s:Button
chromeColor="0x013262"
color="0xFFFFFF"
click="navigator.pushView( com.paypal.example.views.ProductDetails , list.selectedItem );"
enabled="{list.selectedItem != null}"
label="Visualizar" />
<s:Button
chromeColor="0x013262"
color="0x33FF33"
click="application.cart().add( list.selectedItem )"
enabled="{list.selectedItem != null}"
label="Adicionar" />
</s:HGroup>
<s:Button
id="checkoutButton"
skinClass="com.paypal.mobile.CheckoutButton"
enabled="{list.selectedItem != null}"
click="buyNow()"
label="Comprar Agora" />
</s:VGroup>
</s:View>
É nessa View que toda a mágica começa, é aqui que exibimos os produtos ao usuário e permitimos que ele compre diretamente um produto, clicando em “Comprar Agora” ou então damos a ele a possibilidade de adicionar um produto ao carrinho.
Como podem ter visto, temos também um botão “Visualizar”, que permitirá que o usuário saiba mais sobre um produto, veja descrições etc.
ProductDetails.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:View
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
creationComplete="init()"
title="{data.name}">
<fx:Script>
<![CDATA[
import com.paypal.example.Application;
import com.paypal.example.Cart;
private var application :Application = Application.instance();
private var cart :Cart = application.cart();
private function init() :void {
application.loadImage( data.img.@src.toString() , image );
}
private function buyNow() :void {
checkoutButton.enabled = false;
cart.addEventListener( "SUCCESS" , function() :void {
checkoutButton.enabled = true;
navigator.pushView( com.paypal.example.views.PayPalCheckout , cart.token );
cart.clean();
} );
cart.addEventListener( "OPZ" , function() :void {
checkoutButton.enabled = true;
} );
cart.buyNow( XML( data) );
}
]]>
</fx:Script>
<fx:Declarations>
<mx:CurrencyFormatter
id="currencyFormatter"
currencySymbol="R$"
precision="2"
decimalSeparatorFrom=","
decimalSeparatorTo=","
useNegativeSign="true"
useThousandsSeparator="false"
alignSymbol="left" />
</fx:Declarations>
<s:VGroup
width="100%"
height="100%"
horizontalAlign="center"
verticalAlign="middle"
paddingBottom="10">
<s:BitmapImage
id="image"
source="img/app/loader_450x400.gif"
width="450"
height="400" />
<s:Label
text="{data.name.toString()}"
fontSize="30"
fontWeight="bold" />
<s:Label
text="{currencyFormatter.format(data.price)}"
fontSize="20" />
<s:Group width="100%" height="100%">
<s:Label
top="0"
left="10"
text="Descrição:"
fontWeight="bold" />
<s:TextArea
top="20"
left="10"
right="10"
bottom="10"
contentBackgroundColor="0xFFFFFF"
borderVisible="false"
editable="false"
text="{data.description}" />
</s:Group>
<s:HGroup
width="100%"
horizontalAlign="center"
verticalAlign="middle">
<s:Button
chromeColor="0x013262"
color="0x33FF33"
click="application.cart().add( XML( data ) );"
label="Adicionar" />
<s:Button
id="checkoutButton"
skinClass="com.paypal.mobile.CheckoutButton"
click="buyNow()"
label="Comprar Agora" />
</s:HGroup>
</s:VGroup>
</s:View>
Ao contrário da View principal, não temos mais um botão “Visualizar”, claro, dessa vez temos apenas o botão “Comprar Agora” e o botão “Adicionar”, que colocará um produto no carrinho.
O carrinho é bastante simples:
ShoppingCart.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:View
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
title="Meu Carrinho">
<fx:Script>
<![CDATA[
import com.paypal.example.Application;
import com.paypal.example.Cart;
[Bindable]
private var cart :Cart = Application.instance().cart();
[Bindable]
private var products :XML = cart.getItems();
private function checkout() :void {
checkoutButton.enabled = false;
cart.addEventListener( "SUCCESS" , function() :void {
checkoutButton.enabled = true;
navigator.pushView( com.paypal.example.views.PayPalCheckout , cart.token );
cart.clean();
} );
cart.addEventListener( "OPZ" , function() :void {
checkoutButton.enabled = true;
} );
cart.checkout();
}
private function clean() :void {
cart.clean();
navigator.popToFirstView();
}
]]>
</fx:Script>
<fx:Declarations>
<mx:CurrencyFormatter
id="currencyFormatter"
currencySymbol="R$"
precision="2"
decimalSeparatorFrom=","
decimalSeparatorTo=","
useNegativeSign="true"
useThousandsSeparator="false"
alignSymbol="left" />
</fx:Declarations>
<s:VGroup
width="100%"
height="100%"
paddingBottom="20"
paddingTop="10"
horizontalAlign="center"
verticalAlign="top">
<s:List
id="list"
itemRenderer="com.paypal.example.renderers.CartItemRenderer"
width="100%"
height="100%"
borderVisible="false"
selectedIndex="0"
enabled="true"
contentBackgroundColor="0xFFFFFF">
<s:dataProvider>
<s:XMLListCollection source="{products.children()}" />
</s:dataProvider>
</s:List>
<s:HGroup
right="10"
height="40"
verticalAlign="middle">
<s:Label text="TOTAL:" />
<s:Label
text="{currencyFormatter.format(cart.total)}"
fontWeight="bold"
color="0x333333" />
</s:HGroup>
<s:HGroup
width="100%"
horizontalAlign="center"
verticalAlign="middle">
<s:Button
chromeColor="0xFF3333"
color="0xFFFFFF"
click="clean()"
label="Limpar" />
<s:Button
id="checkoutButton"
click="checkout()"
skinClass="com.paypal.mobile.CheckoutButton"
label="Fazer Checkout com" />
</s:HGroup>
</s:VGroup>
</s:View>
Temos um botão “Limpar” para que o cliente possa se desfazer da seleção e o botão “Fazer checkout com PayPal”.
Já na view de checkout, utilizaremos uma WebView para permitir que o usuário acesse o ambiente seguro do PayPal sem sair de nossa aplicação.
Para isso, abriremos uma “porta de visualização” e monitoraremos os redirecionamentos para saber quando o processo foi concluído:
PayPalCheckout.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:View
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
creationComplete="init()"
title="PayPal Checkout">
<fx:Script>
<![CDATA[
private function init() :void {
var webView :StageWebView = new StageWebView();
webView.stage = this.stage;
webView.viewPort = new Rectangle(
0,
78,
stage.stageWidth,
stage.stageHeight
);
webView.loadURL( "https://www.sandbox.paypal.com/br/cgi-bin/webscr?cmd=_express-checkout-mobile&token=" + data );
webView.addEventListener( LocationChangeEvent.LOCATION_CHANGING , function( e :LocationChangeEvent ) :void {
e.preventDefault();
webView.loadURL( e.location );
} );
webView.addEventListener( LocationChangeEvent.LOCATION_CHANGE , function( e :LocationChangeEvent ) :void {
var lastSlash :Number = e.location.lastIndexOf( "/" );
if ( e.location.indexOf( "retorno.php" ) >= 0 ) {
webView.dispose();
webView = null;
navigator.pushView( com.paypal.example.views.Complete );
} else if ( e.location.indexOf( "cancelamento.php" ) >= 0 ) {
webView.dispose();
webView = null;
navigator.pushView( com.paypal.example.views.Canceled );
}
} );
}
]]>
</fx:Script>
</s:View>
Abaixo alguns screenshots da aplicação funcionando no emulador:









A aplicação e o código completo podem ser encontrados em Code Sample
Mensagem do anunciante:
Receba consultoria especializada em WordPress com os melhores profissionais do mercado. Conheça o Apiki WP Consultoria.
Qual a sua opinião?