Pular para o conteúdo principal

Integrando Android e PayPal com Java e MySQL - Parte 2

Na primeira parte deste artigo, nós cobrimos toda a configuração inicial do ambiente, envolvendo principalmente o projeto Java Web com Restful, as bibliotecas e ferramentas envolvidas, bem como suas respectivas versões.


Criamos também toda a comunicação com o banco de dados, o modelo Entidade-Relacionamento, o SQL gerado e as configurações da API do PayPal SDK para Java.

Nessa parte, focaremos em finalizar a aplicação de modelo, principalmente no lado Android, testando tudo de forma local.

Você poderá efetuar o download do código fonte diretamente do meu GitHub, no botão abaixo:

DOWNLOAD CÓDIGO
Na Figura 1 abaixo você pode visualizar como ficarão nossas telas ao final da implementação:

Configurando o projeto Android

Antes de criar o projeto, precisamos efetuar o download do PayPal Android SDK. Extraia os arquivos em uma pasta de preferência.

Mais uma vez, você pode se sentir à vontade para usar o Eclipse Android Bundle ou o AndroidStudio para programar a parte Android, desde que tenha boa experiência em ambas as ferramentas.

Crie um novo projeto Android no Eclipse através das opções File > New > Android Application Project e preencha todas as informações, tais como pacote e nome do projeto.

Copie todo o conteúdo da pasta libs do PayPal Android SDK que você baixou anteriormente e cole na pasta libs do seu projeto recém criado, que deverá ser semelhante à estrutura mostrada na Figura 1.


Figura 1. Criando App PayPal

Abra agora o arquivo colors.xml do seu projeto (ou crie um se não tiver) e adicione o conteúdo da listagem abaixo:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="categoria">#666666</color>
    <color name="valor">#888888</color>
    <color name="list_divisor">#d9d9d9</color>
    <color name="linha_cor_inicio">#ffffff</color>
    <color name="linha_cor_fim">#ffffff</color>
    <color name="linha_cor_inicio_hover">#ebeef0</color>
    <color name="linha_cor_fim_hover">#ebeef0</color>
</resources>

Essas cores serão usadas posteriormente para outras finalidades. Atualize o seu arquivo de strings.xml com algumas chaves que iremos usar nos layouts:

<string name="paypal_client">Cliente PayPal</string>
<string name="checkout">Checkout</string>
<string name="add_carrinho">Add ao Carrinho</string>

Note que na Figura 1 nós estamos fazendo uso de algumas outras bibliotecas como a do Volley, para fazer chamadas a serviços na rede. Baixe a biblioteca e add ao seu classpath.

Crie a classe BitMapCache que será responsável por cachear as requisições via Volley no que se refere às imagens da nossa listagem:

package br.edu.ecommerce.custom.util;

import com.android.volley.toolbox.ImageLoader.ImageCache;

import android.graphics.Bitmap;
import android.support.v4.util.LruCache;

/**
 * Classe responsável por manter o cache das informações de imagens em disco.
 */
public class BitMapCache extends LruCache<String, Bitmap> implements ImageCache {

 public BitMapCache() {
  this(getDefaultCacheSize());
 }

 public BitMapCache(int maxSize) {
  super(maxSize);
 }

 public static int getDefaultCacheSize() {
  final int memoriaMax = (int) (Runtime.getRuntime().maxMemory() / 1024);
  final int tamanhoCache = memoriaMax / 8;

  return tamanhoCache;
 }

 @Override
 protected int sizeOf(String key, Bitmap value) {
  return value.getRowBytes() * value.getHeight() / 1024;
 }

 @Override
 public Bitmap getBitmap(String url) {
  return get(url);
 }

 @Override
 public void putBitmap(String url, Bitmap valor) {
  put(url, valor);
 }

}

Após isso, criaremos também uma classe chamada AppController que será responsável por estender de Application e gerenciar os objetos volley há cada requisição:

package br.edu.ecommerce.custom.app;

import br.edu.ecommerce.custom.util.BitMapCache;

import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.Volley;

import android.app.Application;
import android.text.TextUtils;

public class AppController extends Application {

 private static final String TAG = AppController.class.getSimpleName();

 private static AppController appController;

 private RequestQueue requestQueue;
 private ImageLoader imageLoader;

 @Override
 public void onCreate() {
  super.onCreate();
  appController = this;
 }

 public static synchronized AppController getInstance() {
  return appController;
 }

 public RequestQueue getRequestQueue() {
  if (requestQueue == null) {
   requestQueue = Volley.newRequestQueue(getApplicationContext());
  }
  return requestQueue;
 }

 public ImageLoader getImageLoader() {
  getRequestQueue();
  if (imageLoader == null) {
   imageLoader = new ImageLoader(requestQueue, new BitMapCache());
  }
  return imageLoader;
 }

 public <T> void addToRequestQueue(Request<T> request, String tag) {
  request.setTag(TextUtils.isEmpty(tag) ? TAG : tag);
  getRequestQueue().add(request);
 }

 public <T> void addToRequestQueue(Request<T> request) {
  request.setTag(TAG);
  getRequestQueue().add(request);
 }

 public void cancelarRequestPending(Object tag) {
  if (requestQueue != null) {
   requestQueue.cancelAll(tag);
  }
 }

}

Essas são classes utilitárias, logo você não precisa se preocupar em modificá-las no futuro, mas sim reusá-las quando implementações semelhantes surgirem.

Vamos editar o AndroidManifest.xml agora, com algumas configurações de permissão à APi do PayPal.

<!-- para o card.io e card scanning -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.VIBRATE" />

<uses-feature
    android:name="android.hardware.camera"
    android:required="false" />
<uses-feature
    android:name="android.hardware.camera.autofocus"
    android:required="false" />

<!-- para card.io e paypal -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />

<!-- Dentro da tag application -->
<service
        android:name="com.paypal.android.sdk.payments.PayPalService"
        android:exported="false" />

<activity android:name="br.edu.ecommerce.ListaProdutosActivity" />
    <activity android:name="com.paypal.android.sdk.payments.PaymentActivity" />
<activity android:name="com.paypal.android.sdk.payments.LoginActivity" />
<activity android:name="com.paypal.android.sdk.payments.PaymentMethodActivity" />
<activity android:name="com.paypal.android.sdk.payments.PaymentConfirmActivity" />
<activity
     android:name="io.card.payment.CardIOActivity"
     android:configChanges="keyboardHidden|orientation" />
<activity android:name="io.card.payment.DataEntryActivity" />

Após isso, nós também precisaremos salvar as informações relativas ao PayPal Client ID e Secret no lado Android. Crie a classe Config abaixo que conterá algumas outras constantes importantes:

package br.edu.ecommerce.paypal;

import com.paypal.android.sdk.payments.PayPalConfiguration;

public class Config {

 // PayPal app configuration
 public static final String PAYPAL_CLIENT_ID = "AbkVNdXzruEZ6FEGaJB6TBfB_-4qmlqLPAQj5qin4FI8cS3v0Vs2pjldttP4MgmF487ZyVZ2y34j4xPE";
 public static final String PAYPAL_CLIENT_SECRET = "EDYpA14ZmKc8pXqFe9CHWYJkkqu-6z1NxduRpeIHsr_O37QIgZK7vYd3R6ElsKpsPbQ55VbVo8ps3KuE";

 public static final String PAYPAL_ENVIRONMENT = PayPalConfiguration.ENVIRONMENT_SANDBOX;
 public static final String PAYMENT_INTENT = PayPalPayment.PAYMENT_INTENT_SALE;
 public static final String DEFAULT_CURRENCY = "BRL";

 // PayPal server urls
 public static final String URL_PRODUTOS = "http://192.168.0.103:8080/eCommerce-web/produto/produtos";
 public static final String URL_VER_PAGTOS = "http://192.168.0.103:8080/eCommerce-web/produto/checkPagto";

}

Veja que estamos salvando os mesmos dados que fizemos no lado web, dentro do arquivo de propriedades. Não esqueça de modificar o IP das duas URLs para o da sua máquina, bem como o client id e secret. E nada de tentar usar localhost, não vai funcionar.

Para esse exemplo funcionar, você precisará também duplicar as mesmas classes de entidades que criamos no projeto Java. Não mostraremos esse passo aqui, mas você poderá copiar as classes direto do arquivo de download do código fonte.

Vamos criar agora o arquivo xml de layout da lista de produtos. Para isso, crie o arquivo abaixo dentro da pasta res/layout com o nome de item_lista.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/linha_lista_seletor"
    android:padding="8dp" >

    <!-- Componente de imagem do produto -->

    <com.android.volley.toolbox.NetworkImageView
        android:id="@+id/imgProduto"
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:layout_alignParentLeft="true"
        android:layout_marginRight="8dp" >
    </com.android.volley.toolbox.NetworkImageView>

    <!-- Título do Produto -->

    <TextView
        android:id="@+id/txtTitulo"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignTop="@id/imgProduto"
        android:layout_toRightOf="@id/imgProduto"
        android:textSize="@dimen/titulo"
        android:textStyle="bold" />

    <!-- Qtde de produtos vendidos -->

    <TextView
        android:id="@+id/txtQtde"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/txtTitulo"
        android:layout_marginTop="1dip"
        android:layout_toRightOf="@id/imgProduto"
        android:textSize="@dimen/qtde" />

    <!-- Categorias do produto -->

    <TextView
        android:id="@+id/txtCategoria"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/txtQtde"
        android:layout_marginTop="5dp"
        android:layout_toRightOf="@id/imgProduto"
        android:textColor="@color/categoria"
        android:textSize="@dimen/categoria" />

    <!-- Valor do produto -->

    <TextView
        android:id="@+id/txtValor"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:textColor="@color/valor"
        android:textSize="@dimen/valor" />

    <Button
        android:id="@+id/btnAddToCart"
        android:layout_width="wrap_content"
        android:layout_height="30dp"
        android:layout_below="@id/txtCategoria"
        android:layout_margin="5dp"
        android:layout_toRightOf="@id/imgProduto"
        android:background="#64d048"
        android:paddingLeft="5dp"
        android:paddingRight="5dp"
        android:text="@string/add_carrinho"
        android:textColor="#ffffff" />

</RelativeLayout>

Como estamos lidando com uma listview customizada, precisamos criar uma classe adapter da mesma. Cria a classe CustomListAdapter abaixo e adicione o conteúdo:

package br.edu.ecommerce.custom.adapter;

import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.TextView;
import br.edu.ecommerce.loja_virtual.R;
import br.edu.ecommerce.custom.app.AppController;
import br.edu.ecommerce.custom.model.Produto;

import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.NetworkImageView;

public class CustomListAdapter extends BaseAdapter {

 private Activity activity;
 private List<Produto> produtos = new ArrayList<Produto>();

 private LayoutInflater inflater;

 ImageLoader imageLoader = AppController.getInstance().getImageLoader();
 
 private ProductListAdapterListener listener;

 public CustomListAdapter(Activity activity, List<Produto> produtos, ProductListAdapterListener listener) {
  this.activity = activity;
  this.produtos = produtos;
  this.listener = listener;
 }

 @Override
 public int getCount() {
  return produtos.size();
 }

 @Override
 public Object getItem(int position) {
  return produtos.get(position);
 }

 @Override
 public long getItemId(int position) {
  return position;
 }

 @SuppressLint("InflateParams")
 @Override
 public View getView(int position, View convertView, ViewGroup parent) {
  if (inflater == null) {
   inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
  }

  if (convertView == null) {
   convertView = inflater.inflate(R.layout.linha_lista, null);
  }

  if (imageLoader == null) {
   imageLoader = AppController.getInstance().getImageLoader();
  }

  NetworkImageView imageProd = (NetworkImageView) convertView.findViewById(R.id.imgProduto);
  TextView txtTitulo = (TextView) convertView.findViewById(R.id.txtTitulo);
  TextView txtQtde = (TextView) convertView.findViewById(R.id.txtQtde);
  TextView txtCategoria = (TextView) convertView.findViewById(R.id.txtCategoria);
  TextView txtValor = (TextView) convertView.findViewById(R.id.txtValor);

  // Recuperando o produto atual da iteração
  final Produto prod = produtos.get(position);

  imageProd.setImageUrl(prod.getUrlImg(), imageLoader);

  txtTitulo.setText(prod.getTitulo());
  txtQtde.setText("Qtde Vendidos: " + String.valueOf(prod.getQtde()));
  NumberFormat format = NumberFormat.getCurrencyInstance(new Locale("pt"));
  txtValor.setText("R$ " + String.valueOf(format.format(prod.getValor())));

  String concat = "";
  for (String cat : prod.getCategorias()) {
   concat += cat + ", ";
  }
  concat = concat.length() > 0 ? concat.substring(0, concat.length() - 2) : concat;

  txtCategoria.setText(concat);

  Button btnAddToCart = (Button) convertView.findViewById(R.id.btnAddToCart);

  btnAddToCart.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
    listener.onAddCarrrinhoPressed(prod);
   }
  });

  return convertView;
 }

 public interface ProductListAdapterListener {
  public void onAddCarrrinhoPressed(Produto produto);
 }

}

Após isso, nós também precisamos criar o xml que iterará todos os itens customizados dos produtos. Para isso, crie o arquivo listagem_produtos.xml também dentro da pasta res/layout com o seguinite conteúdo:
<?xml version="1.0" encoding="UTF-8"?>
<relativelayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_height="match_parent" android:layout_width="match_parent" tools:context=".ListaProdutosActivity">
   <listview android:divider="@color/list_divisor" android:dividerheight="1dp" android:id="@+id/listaProd" android:layout_height="wrap_content" android:layout_width="match_parent" android:listselector="@drawable/linha_lista_seletor">
      <button android:background="#428bca" android:id="@+id/btnCheckout" android:layout_alignparentbottom="true" android:layout_height="wrap_content" android:layout_width="fill_parent" android:text="@string/checkout" />
   </listview>
</relativelayout>

Estamos quase lá. Agora precisamos criar a Activity que conterá os métodos de fachada para lidar com a comunicação com a API do PayPal. Veja o código abaixo:

package br.edu.ecommerce;

import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.ListView;
import android.widget.SearchView;
import android.widget.Toast;
import br.edu.devmedia.loja_virtual.R;
import br.edu.ecommerce.custom.UTF8ParseJson;
import br.edu.ecommerce.custom.adapter.CustomListAdapter;
import br.edu.ecommerce.custom.adapter.CustomListAdapter.ProductListAdapterListener;
import br.edu.ecommerce.custom.app.AppController;
import br.edu.ecommerce.custom.model.Produto;
import br.edu.ecommerce.paypal.Config;

import com.android.volley.DefaultRetryPolicy;
import com.android.volley.Request.Method;
import com.android.volley.Response;
import com.android.volley.Response.ErrorListener;
import com.android.volley.Response.Listener;
import com.android.volley.RetryPolicy;
import com.android.volley.VolleyError;
import com.android.volley.VolleyLog;
import com.android.volley.toolbox.JsonArrayRequest;
import com.android.volley.toolbox.StringRequest;
import com.google.gson.Gson;
import com.paypal.android.sdk.payments.PayPalConfiguration;
import com.paypal.android.sdk.payments.PayPalItem;
import com.paypal.android.sdk.payments.PayPalPayment;
import com.paypal.android.sdk.payments.PayPalPaymentDetails;
import com.paypal.android.sdk.payments.PayPalService;
import com.paypal.android.sdk.payments.PaymentActivity;
import com.paypal.android.sdk.payments.PaymentConfirmation;

public class ListaProdutosActivity extends Activity implements SearchView.OnQueryTextListener, ProductListAdapterListener {

 private static final String TAG = ListaProdutosActivity.class.getSimpleName();

 private ListView lstProd;
 private List<Produto> produtos = new ArrayList<Produto>();
 private CustomListAdapter adapter;

 private ProgressDialog progressDialog;

 // To store the products those are added to cart
 private List<PayPalItem> produtosCarrinho = new ArrayList<PayPalItem>();

 private Button btnCheckout;

 private static final int CODIGO_PAGTO = 1;

 // PayPal configuration
 private static PayPalConfiguration paypalConfig = 
   new PayPalConfiguration().environment(Config.PAYPAL_ENVIRONMENT).clientId(Config.PAYPAL_CLIENT_ID).languageOrLocale("pt_BR");

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.listagem_produtos);

  lstProd = (ListView) findViewById(R.id.listaProd);
  btnCheckout = (Button) findViewById(R.id.btnCheckout);

  adapter = new CustomListAdapter(this, produtos, this);
  lstProd.setAdapter(adapter);

  progressDialog = new ProgressDialog(this);
  progressDialog.setCancelable(false);
  progressDialog.setMessage("Carregando...");
  progressDialog.show();

  getActionBar().setBackgroundDrawable(new ColorDrawable(Color.parseColor("#1b1b1b")));

  // Starting PayPal service
  Intent intent = new Intent(this, PayPalService.class);
  intent.putExtra(PayPalService.EXTRA_PAYPAL_CONFIGURATION, paypalConfig);
  startService(intent);

  // Checkout button click listener
  btnCheckout.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
    // Check for empty cart
    if (produtosCarrinho.size() > 0) {
     executarPagtoPayPal();
    } else {
     Toast.makeText(getApplicationContext(), "Carrinho vazio! Favor adicionar produtos!", Toast.LENGTH_SHORT).show();
    }

   }
  });

  // Fetching products from server
  executarRequestProdutos(null);
 }

 private void executarRequestProdutos(String param) {
  produtos.clear();
  if (param != null) {
   try {
    param = URLEncoder.encode(param, "UTF-8");
   } catch (UnsupportedEncodingException e) {
    e.printStackTrace();
   }
  }
  JsonArrayRequest requestProds = new UTF8ParseJson(param != null ? Config.URL_PRODUTOS + "?str=" + param : Config.URL_PRODUTOS, new Listener<JSONArray>() {
   @Override
   public void onResponse(JSONArray jsonArray) {
    esconderDialog();

    for (int i = 0; i < jsonArray.length(); i++) {
     try {
      JSONObject jsonObj = jsonArray.getJSONObject(i);

      Gson gson = new Gson();
      Produto produto = gson.fromJson(jsonObj.toString(), Produto.class);

      produtos.add(produto);
     } catch (JSONException e) {
      e.printStackTrace();
     }
    }
    adapter.notifyDataSetChanged();
   }
  }, 
  new ErrorListener() {
   @Override
   public void onErrorResponse(VolleyError error) {
    VolleyLog.d(TAG, "Erro: " + error.getMessage());
    esconderDialog();
   }
  });
  AppController.getInstance().addToRequestQueue(requestProds);
 }

 private void esconderDialog() {
  if (progressDialog != null) {
   progressDialog.dismiss();
   progressDialog = null;
  }
 }

 @Override
 protected void onDestroy() {
  super.onDestroy();
  esconderDialog();
 }

 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
  getMenuInflater().inflate(R.menu.produtos, menu);

  return true;
 }

 @Override
 public boolean onQueryTextSubmit(String query) {
  return false;
 }

 @Override
 public boolean onQueryTextChange(String newText) {
  if (newText != null) {
   executarRequestProdutos(newText);
  }
  return false;
 }

 /**
  * Verifying the mobile payment on the server to avoid fraudulent payment
  * */
 private void verificarPagtoServidor(final String idPagto, final String jsonClientePagto) {
  // Showing progress dialog before making request
  progressDialog = new ProgressDialog(this);
  progressDialog.setMessage("Verificando pagamento...");
  progressDialog.show();

  StringRequest verifyReq = new StringRequest(Method.POST, Config.URL_VER_PAGTOS, new Response.Listener<String>() {
   @Override
   public void onResponse(String response) {
    Log.d(TAG, "verify payment: " + response.toString());

    try {
     JSONObject res = new JSONObject(response);
     boolean erro = res.getBoolean("erro");
     String msg = res.getString("msg");

     // user error boolean flag to check for errors
     Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();

     if (!erro) {
      // empty the cart
      produtosCarrinho.clear();
     }

    } catch (JSONException e) {
     e.printStackTrace();
    }

    // hiding the progress dialog
    esconderDialog();
   }
  }, new Response.ErrorListener() {
   @Override
   public void onErrorResponse(VolleyError error) {
    Log.e(TAG, "Erro de verificação: " + error.getMessage());
    Toast.makeText(getApplicationContext(), error.getMessage(), Toast.LENGTH_SHORT).show();
    // hiding the progress dialog
    esconderDialog();
   }
  }) {
   @Override
   protected Map<String, String> getParams() {
    SharedPreferences preferences = getPreferences(MODE_PRIVATE);
    
    Integer idUsuario = preferences.getInt("usuario", 0);
    
    Map<String, String> params = new HashMap<String, String>();
    params.put("idPagto", idPagto);
    params.put("idUsuario", idUsuario != 0 ? String.valueOf(idUsuario) : "1");
    params.put("jsonClientePagto", jsonClientePagto);

    return params;
   }
  };

  // Setting timeout to volley request as verification request takes sometime
  int socketTimeout = 60000;
  RetryPolicy policy = new DefaultRetryPolicy(socketTimeout,
    DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
    DefaultRetryPolicy.DEFAULT_BACKOFF_MULT);
  verifyReq.setRetryPolicy(policy);

  // Adding request to request queue
  AppController.getInstance().addToRequestQueue(verifyReq);
 }

 /**
  * Preparing final cart amount that needs to be sent to PayPal for payment
  * */
 private PayPalPayment prepararCarrinhoFinal() {
  PayPalItem[] itens = new PayPalItem[produtosCarrinho.size()];
  itens = produtosCarrinho.toArray(itens);

  // Total amount
  BigDecimal subtotal = PayPalItem.getItemTotal(itens);

  // If you have frete cost, add it here
  BigDecimal frete = new BigDecimal("0.0");

  // If you have tax, add it here
  BigDecimal taxa = new BigDecimal("0.0");

  PayPalPaymentDetails detalhesPagto = new PayPalPaymentDetails(frete, subtotal, taxa);

  BigDecimal quantia = subtotal.add(frete).add(taxa);

  PayPalPayment pagto = new PayPalPayment(
    quantia,
    Config.DEFAULT_CURRENCY,
    "Transação de compra na Loja DevMedia sendo processada...",
    Config.PAYMENT_INTENT);

  pagto.items(itens).paymentDetails(detalhesPagto);

  // Custom field like invoice_number etc.,
  pagto.custom("Texto a ser associado com o pagto que a app vai usar.");

  return pagto;
 }

 /**
  * Launching PalPay payment activity to complete the payment
  * */
 private void executarPagtoPayPal() {
  PayPalPayment coisasPraComprar = prepararCarrinhoFinal();

  Intent intent = new Intent(ListaProdutosActivity.this, PaymentActivity.class);

  intent.putExtra(PayPalService.EXTRA_PAYPAL_CONFIGURATION, paypalConfig);

  intent.putExtra(PaymentActivity.EXTRA_PAYMENT, coisasPraComprar);

  startActivityForResult(intent, CODIGO_PAGTO);
 }

 @Override
 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  if (requestCode == CODIGO_PAGTO) {
   if (resultCode == Activity.RESULT_OK) {
    PaymentConfirmation confirm = data.getParcelableExtra(PaymentActivity.EXTRA_RESULT_CONFIRMATION);
    if (confirm != null) {
     try {
      Log.e(TAG, confirm.toJSONObject().toString(4));
      Log.e(TAG, confirm.getPayment().toJSONObject().toString(4));

      String pagtoId = confirm.toJSONObject().getJSONObject("response").getString("id");

      String jsonClientePagto = confirm.getPayment().toJSONObject().toString();

      Log.e(TAG, "pagtoId: " + pagtoId + ", jsonClientePagto: " + jsonClientePagto);

      // Now verify the payment on the server side
      verificarPagtoServidor(pagtoId, jsonClientePagto);
     } catch (JSONException e) {
      Log.e(TAG, "Um erro ocorreu: ", e);
     }
    }
   } else if (resultCode == Activity.RESULT_CANCELED) {
    Log.e(TAG, "O usuário cancelou a operação.");
   } else if (resultCode == PaymentActivity.RESULT_EXTRAS_INVALID) {
    Log.e(TAG, "Um pagamento inválido foi submetido.");
   }
  }
 }

 @Override
 public void onAddCarrrinhoPressed(Produto produto) {
  PayPalItem item = new PayPalItem(produto.getTitulo(), 1,
    new BigDecimal(produto.getValor()).setScale(2, RoundingMode.CEILING), Config.DEFAULT_CURRENCY, produto.getSku());

  produtosCarrinho.add(item);

  Toast.makeText(getApplicationContext(), item.getName() + " adicionado ao carrinho!", Toast.LENGTH_SHORT).show();
 }
}

Agora é só executar ambos os aplicativos (Restful e Android) e testar o projeto. O resultado deverá ser semelhante à Figura 2.


Figura 2. Criando App PayPal

O fluxo de testes é simples, e pode ser bem explicado pelas imagens acima.

Então, é isso pessoal. Se tiverem alguma dúvida ou problemas com a implementação é só comentar que venho ajudar vocês! Bons estudos! :)

-->

Comentários

  1. Parabéns pela iniciativa, estou tirando varias dúvidas com seu artigo.

    ResponderExcluir
  2. Olá Diogo,
    Parabéns pelo seu trabalho, ficou perfeito.
    Gostaria de tirar uma dúvida se possível.
    No meu caso tenho vários vendedores na aplicação, e quando pego a credencial do Client ID em (My Apps & Credentials) esta vinculada a um único vendedor. Teria que criar varios "App Name" pra vincular cada vendedor e ter varias credenciais ??
    Fiz o teste acima e deu certo, mas não sei se é o ideal.
    Outra maneira foi de pegar a credencial do usuário Vendedor em (Accounts/Profile/API Credentials) mas não funcionou.

    ResponderExcluir
    Respostas
    1. Opa Renato, tudo bem?

      Que bom que gostou do post. Então, sempre que precisar inicializar a API precisa passar o ID do vendor no PayPal para efetuar o pagamento, logo, se precisar de vários vendedores, cada um deles vai precisar ter uma conta equivalente no PayPal.

      No seu caso, você pode solicitar os id's de cada um e salvar numa base no banco e recuperar os valores via Web Service sempre que solicitado, comunicando diretamente com a API do Android, entendeu?

      Excluir
    2. Perfeito Diogo, entendi como funciona o processo. Estou armazenando as chaves de cada vendedor no BD.
      Muito obrigado pela ajuda

      Excluir
  3. Olá Diogo, a integração ficou ótima e está em produção. Mas estou com um problema sério, porque esta versão "paypal android sdk" é uma versão americana e não consigo realizar parcelamento.
    O que parece é que, parcelamento é específico para o Brasil. E o Paypal do Brasil não da suporte a esta versão, pois já entrei em contato com eles.

    Você tem esse conhecimento pra me ajudar? já realizou parcelamento pra essa versão?

    Muito Obrigado

    ResponderExcluir
    Respostas
    1. Opa Renato, tudo bem?

      Na verdade, infelizmente, algumas features não são liberadas para certos países e isso incluir parcelamento. :(

      O jeito é usar outra solução, como o Mercado Pago, a solução deles é muito boa.

      Excluir
  4. Diogo blz ?

    Preciso integrar o Mercado Pago em uma aplicação do android e não estou conseguindo , teria como me ajudar , nunca fiz isso então não estou entendendo a documentação deles

    ResponderExcluir
  5. Fala Peterson, tudo bem?

    Infelizmente estou sem tempo para formular um artigo novo sobre Mercado Pago, mas tenho um artigo na DevMedia que editei que fala sobre isso. Dá uma olhada: http://www.devmedia.com.br/criando-uma-mini-loja-virtual-com-phonegap-parte-5/36754

    :)

    ResponderExcluir
    Respostas
    1. Pra implementar o Paypal , seguindo seu tutorial sera que funciona ainda , pois como foi postado em 2015 , deve ter mudado algumas coisas certo ?

      Excluir
    2. Sim, a maioria dos conceitos ainda estão recentes, no máximo alguma versão de biblioteca que está mais recente, mas ainda funciona sim.. :)

      Excluir
  6. Parabéns, muito legal seu artigo, mas preciso de uma ajuda quando vou pagar com cartão não funciona, está dando erro de currency

    ResponderExcluir
    Respostas
    1. Opa, tudo bem?

      Então, isso é um problema na verdade do PayPal, veja que pagamentos com cartão só estão habilitados para algumas moedas, o que não é o caso do Real. :(

      Apenas as seguintes: USD, GBP, CAD, EUR e JPY. Veja: https://github.com/paypal/PayPal-iOS-SDK#currencies

      Excluir
  7. Diego, como eu consigo esse código do "import br.edu.devmedia.loja_virtual.R;" é um curso que você dá? pois não esta no download pelo menos eu não encontrei.

    ResponderExcluir

Postar um comentário

Postagens mais visitadas deste blog

Integrando Android e PayPal com Java e MySQL - Parte 1

Uma das funcionalidades mais importantes da maioria dos apps mobile de hoje em dia é a possibilidade de se integrar com plataformas de pagamento online, tais como PagSeguro, MercadoPago ou a mais famosa de todas (a nível internacional): PayPal . Na maioria dos apps de eCommerce, que se caracterizam principalmente por compras e vendas online, não basta somente ter uma boa interface e lógica de negócio implementadas, é também preciso gerenciar tudo isso de forma segura, e para isso precisamos fazer uso de Web Services, bancos de dados, aplicações e outros tipos de recursos e operações no lado do servidor. Com o objetivo de cobrir uma implementação pouco vista em português, esse artigo, dividido em duas partes, visa ensinar como construir uma aplicação básica em Android , usando a biblioteca SDK do PayPal , uma integração server side com um projeto em Java Web , que fará uso de requisições HTTP via Web Services Restful (implementação Jersey ) e salvará os dados em um schema MySQ

Como acessar um iframe e seus elementos via jQuery?

Recentemente tive  um problema no projeto pois sentiu a necessidade de acessar um valor de um input que estava dentro de um iframe. Esse tipo de situação não é tão comum, uma vez que geralmente acessamos os valores do iframe para fora. Para acessar, de dentro de um iframe, um valor externo, utilizamos o seguinte código: $('#idDoElementoExterno', parent.document).val(); Entretanto, nunca tínhamos passado pela situação contrária. Pesquisando um pouco descobrimos uma alternativa, porém em JavaScript. Para ficar melhor o entendimento, vamos simular uma situação aqui. Temos uma página html "A.html" e dentro da mesma existe um iframe que aponta (src) para uma página "B.html": <!-- A.html --> <html> <head> <title>Testando iframe - jQuery</title> <script language="JavaScript"> function exibeValor() { // alert aqui! } </script> </head> <body> <input typ

"Content is not allowed in prolog" - Entendendo exceção no Seam

Recentemente tive um problema de edição em um arquivo .xhtml utilizando JBoss Seam, Richfaces e afins. A princípio a mensagem de erro não dizia muito a respeito da causa do mesmo: com.sun.facelets.FaceletException: Error Parsing /consulta.xhtml: Error Traced[line: 1] Content is not allowed in prolog. "O conteúdo não é permitido no prólogo". Mas que conteúdo? Em qual prolog? Depois de dar uma pesquisada descobri que o erro acontece em vista de terem sido colocados alguns caraceteres inválidos antes da declaração de documento xml na página xhtml. Em outras palavras, a primeira coisa que deve constar em um documento xml (afins) deve ser: <?xml version="1.0" encoding="utf-8"?> Qualquer coisa antes disso, até mesmo um simples espaço em branco, pode gerar o erro em questão. Por fim, lembre-se de que a declaração de documento xml segue o padrão de encoding definido. Logo temos: <!-- Inc