Tích hợp cổng thanh toán Paypal vào ứng dụng Android sử dụng PHP, MySQL (Phần 2)

Trong phần trước Tích hợp cổng thanh toán Paypal vào ứng dụng Android sử dụng PHP, MySQL (Phần 1) , chúng ta đã tích hợp PayPal phía máy chủ tức là tạo ra cơ sở dữ liệu mysql và viết các API PHP để tương tác với ứng dụng Android và PayPal API REST.

Trong phần này chúng ta sẽ làm tiếp phần còn lại đó là xây dựng ứng dụng Android và tích hợp cổng thanh toán PayPal. Sau đó chúng ta sẽ thực hiện một vài bài kiểm tra trong môi trường sandbox.

9. Tải về PayPal Android SDK

Cũng giống như PayPal PHP REST API SDK, PayPal cung cấp SDK cho nền tảng di động. Tải SDK PayPal Android và giải nén nó. Sau khi giải nén bạn sẽ thấy một ứng dụng mẫu, tài liệu và thư viện. Chúng ta sẽ libs. Nếu bạn muốn thử thanh toán với hình thức Future PaymentsProfile Sharing, bạn có thể tìm code hướng dẫn trong ứng dụng mẫu.

10. Tạo Android project

Bây giờ chúng ta sẽ cùng nhau xây dựng Android project

  1. Tạo một dự án mới trong Eclipse bằng cách vào File ⇒ New ⇒ Android Application Project và điền vào các thông tin cần thiết.

Tôi đã đặt tên dự án của tôi như PayPalClient và tên package là info.androidhive.paypalclient

  1. Bây giờ tôi tạo thêm hai package có tên là apphelper.

  2. Paste tất cả các nội dung của thư viện PayPal Android SDK của thư mục vào thư mục libs của dự án.

Dưới đây là cấu trúc dự án mà chúng ta sẽ tạo ra. Bạn cần phải có tất cả các file lib PayPal và các gói đặt như hình dưới đây.

project_struct

  1. Mở file colors.xmlres ⇒ value và thêm vào các định nghĩa màu mới

colors.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="btn_bg_checkout">#428bca</color>
    <color name="list_divider">#dedede</color>
    <color name="white">#ffffff</color>
    <color name="lbl_product_name">#333333</color>
    <color name="lbl_product_description">#444444</color>
    <color name="bg_msg_you">#5eb964</color>
    <color name="bg_msg_from">#e5e7eb</color>
    <color name="msg_border_color">#a1a1a1</color>
    <color name="bg_btn_join">#1e6258</color>
    <color name="bg_msg_input">#e8e8e8</color>
    <color name="text_msg_input">#626262</color>
    <color name="lblFromName">#777777</color>
</resources>

  1. Open strings.xmlres ⇒ value và thêm vào những chuỗi giá trị mới
strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="app_name">PalPal Client</string>
    <string name="checkout">Checkout</string>
    <string name="add_to_cart">Add to Cart</string>

</resources>

  1. Tôi đang sử dụng thư viện volley để thực hiện cuộc gọi API. Tải volley.jar và paste nó vào thư mục libs. (Nếu bạn là người mới dùng thư viện Volley, Hướng dẫn này sẽ đưa đến cho bạn một cái nhìn tốt về thư viện Volley).

  2. Tạo lớp có tên LruBitmapCache.java trong package helper. Mục đích của lớp học này để cache ảnh tải về.

LruBitmapCache.java
package info.androidhive.palpalclient.helper;

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

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

public class LruBitmapCache extends LruCache<String, Bitmap> implements
        ImageCache {
    public static int getDefaultLruCacheSize() {
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        final int cacheSize = maxMemory / 8;

        return cacheSize;
    }

    public LruBitmapCache() {
        this(getDefaultLruCacheSize());
    }

    public LruBitmapCache(int sizeInKiloBytes) {
        super(sizeInKiloBytes);
    }

    @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 bitmap) {
        put(url, bitmap);
    }
}

  1. Crete một lớp có tên AppController.java trong gói app. Đây là một lớp singleton được mở rộng từ lớp Application, lớp này sẽ được khởi chạy từ từ khi ứng dụng bắt đầu chạy. Tất cả việc khởi tạo các đối tượng volley sẽ được thực hiện ở đây.

AppController.java
package info.androidhive.palpalclient.app;

import info.androidhive.palpalclient.helper.LruBitmapCache;
import android.app.Application;
import android.text.TextUtils;

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

public class AppController extends Application {

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

    private RequestQueue mRequestQueue;
    private ImageLoader mImageLoader;

    private static AppController mInstance;

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

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

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

        return mRequestQueue;
    }

    public ImageLoader getImageLoader() {
        getRequestQueue();
        if (mImageLoader == null) {
            mImageLoader = new ImageLoader(this.mRequestQueue,
                    new LruBitmapCache());
        }
        return this.mImageLoader;
    }

    public <T> void addToRequestQueue(Request<T> req, String tag) {
        // set the default tag if tag is empty
        req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);
        getRequestQueue().add(req);
    }

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

    public void cancelPendingRequests(Object tag) {
        if (mRequestQueue != null) {
            mRequestQueue.cancelAll(tag);
        }
    }
}

  1. Bây giờ chỉnh sửa AndroidManifest.xml theo chỉ dẫn ở dưới đây.

Kai báo AppController để trong thẻ <application> sử dụng android:name sở hữu tên.

Thêm các quyền CAMERA, VIBRATE, ACCESS_NETWORK_STATE, INTERNET.

Thêm các tính năng camera. Điều này là cần thiết khi người dùng muốn thanh toán bằng tính năng card.io**.

Thêm PayPalService

Thêm các PayPal SDK activities (PaymentActivity, LoginActivity, PaymentMethodActivity, PaymentConfirmActivity và CardIOActivity). Đây là những activity cần thiết để thực hiện thanh toán paypal.

Cuối cùng AndroidManifest.xml sẽ giống như dưới đây.

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="info.androidhive.palpalclient"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="21" />

    <!-- for card.io 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" />

    <!-- for most things, including card.io & paypal -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:name="info.androidhive.palpalclient.app.AppController"
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:name="info.androidhive.palpalclient.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service
            android:name="com.paypal.android.sdk.payments.PayPalService"
            android:exported="false" />

        <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" />
    </application>

</manifest>

  1. Tạo một lớp có tên Config.java trong gói app. Lớp này chứa tất cả các biến cấu hình như PayPal client id & secret, môi trường paypal và url endpoint để truy cập vào máy chủ mà chúng tôi xây dựng trong phần đầu tiên của loạt bài này.

Config.java
package info.androidhive.palpalclient.app;

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

public class Config {

    // PayPal app configuration
    public static final String PAYPAL_CLIENT_ID = "AbLgy0hRsq0PmoGK-ws2-jlBIeBVKUUU0xRjbfW1-GAckylz_TDNsh1cMrIiSksc2wpqYC2PisTrKhko";
    public static final String PAYPAL_CLIENT_SECRET = "";

    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 = "USD";

    // PayPal server urls
    public static final String URL_PRODUCTS = "http://192.168.0.103/PayPalServer/v1/products";
    public static final String URL_VERIFY_PAYMENT = "http://192.168.0.103/PayPalServer/v1/verifyPayment";

}

  1. Tạo một lớp model có tên Product.java trong package helper. Lớp này sẽ được sử dụng trong khi phân tích các sản phẩm trả về nằm trong chuỗi json.

Product.java
package info.androidhive.palpalclient.helper;

import java.math.BigDecimal;

public class Product {
    private String id, name, description, image, sku;
    private BigDecimal price;

    public Product() {

    }

    public Product(String id, String name, String description, String image,
            BigDecimal price, String sku) {
        this.id = id;
        this.name = name;
        this.description = description;
        this.image = image;
        this.price = price;
        this.sku = sku;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getImage() {
        return image;
    }

    public void setImage(String image) {
        this.image = image;
    }

    public BigDecimal getPrice() {
        return price;
    }

    public void setPrice(BigDecimal price) {
        this.price = price;
    }

    public String getSku() {
        return sku;
    }

    public void setSku(String sku) {
        this.sku = sku;
    }

}

  1. Theo res ⇒ layout, tạo ra một tập tin có tên list_item_product.xml. File giao diện này sẽ vẽ một row trong danh sách các sản phẩm. Cách bố trí này có chứa hình ảnh sản phẩm ở bên trái là các chi tiết như tên sản phẩm, mô tả, giá cả ở bên phải.

list_item_product.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="match_parent"
    android:background="@color/white"
    android:paddingBottom="10dp" >

    <com.android.volley.toolbox.NetworkImageView
        android:id="@+id/productImage"
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_margin="8dp"
        android:scaleType="fitCenter" >
    </com.android.volley.toolbox.NetworkImageView>

    <TextView
        android:id="@+id/productName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/productImage"
        android:padding="5dp"
        android:textColor="@color/lbl_product_name"
        android:textSize="16dp"
        android:textStyle="bold" />

    <TextView
        android:id="@+id/productDescription"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/productName"
        android:layout_toRightOf="@id/productImage"
        android:padding="5dp"
        android:textColor="@color/lbl_product_description" />

    <TextView
        android:id="@+id/productPrice"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/productDescription"
        android:layout_toRightOf="@id/productImage"
        android:padding="5dp"
        android:textColor="@color/lbl_product_description" />

    <Button
        android:id="@+id/btnAddToCart"
        android:layout_width="wrap_content"
        android:layout_height="30dp"
        android:layout_below="@id/productPrice"
        android:layout_margin="5dp"
        android:layout_toRightOf="@id/productImage"
        android:background="#64d048"
        android:paddingLeft="5dp"
        android:paddingRight="5dp"
        android:text="@string/add_to_cart"
        android:textColor="@color/white" />

</RelativeLayout>

  1. Khi xem danh sách sản phẩm được tùy biến, chúng ta cần phải viết các adapter để cài đặt việc hiển thị sản phẩm. Tạo một lớp có tên ProductListAdapter.java trong package helper.

ProductListAdapter.java
package info.androidhive.palpalclient.helper;

import info.androidhive.palpalclient.R;
import info.androidhive.palpalclient.app.AppController;

import java.util.List;

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 com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.NetworkImageView;

public class ProductListAdapter extends BaseAdapter {
    private Activity activity;
    private LayoutInflater inflater;
    private List<Product> products;
    private ProductListAdapterListener listener;
    ImageLoader imageLoader = AppController.getInstance().getImageLoader();

    public ProductListAdapter(Activity activity, List<Product> feedItems,
            ProductListAdapterListener listener) {
        this.activity = activity;
        this.products = feedItems;
        this.listener = listener;
    }

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

    @Override
    public Object getItem(int location) {
        return products.get(location);
    }

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

    @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.list_item_product, null);

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

        TextView name = (TextView) convertView.findViewById(R.id.productName);
        TextView description = (TextView) convertView
                .findViewById(R.id.productDescription);
        TextView price = (TextView) convertView.findViewById(R.id.productPrice);

        NetworkImageView image = (NetworkImageView) convertView
                .findViewById(R.id.productImage);

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

        final Product product = products.get(position);

        name.setText(product.getName());

        description.setText(product.getDescription());

        price.setText("Price: $" + product.getPrice());

        // user profile pic
        image.setImageUrl(product.getImage(), imageLoader);

        btnAddToCart.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                listener.onAddToCartPressed(product);
            }
        });

        return convertView;
    }

    public interface ProductListAdapterListener {
        public void onAddToCartPressed(Product product);
    }

}

  1. Bây giờ tất cả các lớp helper dã sẵn sàng. Bây giờ chúng ta sẽ chuyển qua activity chính và bắt đầu với việc thêm code cho Paypal. Tạo một file giao diện có tên activity_main.xml trong thư mục res/layout và thêm mã dưới đây. Cách bố trí này có chứa một ListView để hiển thị danh sách sản phẩm và một nút để làm ckeckout.

activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white" >

    <ListView
        android:id="@+id/list"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:paddingBottom="20dp"
        android:divider="@color/list_divider"
        android:dividerHeight="1dp">
    </ListView>

    <Button
        android:id="@+id/btnCheckout"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:text="@string/checkout"
        android:background="@color/btn_bg_checkout"/>

</RelativeLayout>

  1. Mở MainActivity.java của bạn và thực hiện các sau thay đổi. Về cơ bản để hỗ trợ thêm PayPal, chúng ta cần phải thực hiện các bước đơn giản dưới đây .

Tạo đối tượng PayPalConfiguration bằng cách thiết lập cấu hình cần thiết như môi trường và client id.

Khởi chạy PayPalService trong onCreate()

Khi người dùng nhấn nút kiểm tra, khởi chạy PaymentActivity bằng cách gửi các thông tin cần thiết như sản phẩm cuối cùng, giá cả, mô tả, vv

Sau khi quá trình thanh toán được thực hiện, tiếp nhận các PaymentConfirmation trong onActivityResult(). Đây là nơi bạn sẽ nhận được kết quả từ paypal như id thanh toán và các thông tin quan trọng khác.

Cuối cùng gửi id thanh toán và chuỗi json kết quả với máy chủ của chúng ta để xác minh lại từ phía máy chủ.

Trong đoạn code dưới đây

fetchProducts() - lấy về các sản phẩm từ máy chủ của chúng ta dạng json.

verifyPaymentOnServer() - Thẩm tra việc thanh toán di động trên máy chủ.

prepareFinalCart() - Chuẩn bị các thông tin như tổng số tiền, vật phẩm cần phải được gửi để hoạt động thanh toán paypal.

launchPayPalPayment() - chạy hoạt động thanh toán PayPal

onAddToCartPressed() - Thêm các mục vào giỏ hàng khi nhấn nút thêm.


MainActivity.java
package info.androidhive.palpalclient;

import info.androidhive.palpalclient.app.AppController;
import info.androidhive.palpalclient.app.Config;
import info.androidhive.palpalclient.helper.Product;
import info.androidhive.palpalclient.helper.ProductListAdapter;
import info.androidhive.palpalclient.helper.ProductListAdapter.ProductListAdapterListener;

import java.math.BigDecimal;
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.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;

import com.android.volley.DefaultRetryPolicy;
import com.android.volley.Request.Method;
import com.android.volley.Response;
import com.android.volley.RetryPolicy;
import com.android.volley.VolleyError;
import com.android.volley.VolleyLog;
import com.android.volley.toolbox.JsonObjectRequest;
import com.android.volley.toolbox.StringRequest;
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 MainActivity extends Activity implements
        ProductListAdapterListener {
    private static final String TAG = MainActivity.class.getSimpleName();

    private ListView listView;
    private Button btnCheckout;

    // To store all the products
    private List<Product> productsList;

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

    private ProductListAdapter adapter;

    // Progress dialog
    private ProgressDialog pDialog;

    private static final int REQUEST_CODE_PAYMENT = 1;

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

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

        listView = (ListView) findViewById(R.id.list);
        btnCheckout = (Button) findViewById(R.id.btnCheckout);

        productsList = new ArrayList<Product>();
        adapter = new ProductListAdapter(this, productsList, this);

        listView.setAdapter(adapter);

        pDialog = new ProgressDialog(this);
        pDialog.setCancelable(false);

        // 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 (productsInCart.size() > 0) {
                    launchPayPalPayment();
                } else {
                    Toast.makeText(getApplicationContext(), "Cart is empty! Please add few products to cart.",
                            Toast.LENGTH_SHORT).show();
                }

            }
        });

        // Fetching products from server
        fetchProducts();
    }

    /**
     * Fetching the products from our server
     * */
    private void fetchProducts() {
        // Showing progress dialog before making request

        pDialog.setMessage("Fetching products...");

        showpDialog();

        // Making json object request
        JsonObjectRequest jsonObjReq = new JsonObjectRequest(Method.GET,
                Config.URL_PRODUCTS, null, new Response.Listener<JSONObject>() {

                    @Override
                    public void onResponse(JSONObject response) {
                        Log.d(TAG, response.toString());

                        try {
                            JSONArray products = response
                                    .getJSONArray("products");

                            // looping through all product nodes and storing
                            // them in array list
                            for (int i = 0; i < products.length(); i++) {

                                JSONObject product = (JSONObject) products
                                        .get(i);

                                String id = product.getString("id");
                                String name = product.getString("name");
                                String description = product
                                        .getString("description");
                                String image = product.getString("image");
                                BigDecimal price = new BigDecimal(product
                                        .getString("price"));
                                String sku = product.getString("sku");

                                Product p = new Product(id, name, description,
                                        image, price, sku);

                                productsList.add(p);
                            }

                            // notifying adapter about data changes, so that the
                            // list renders with new data
                            adapter.notifyDataSetChanged();

                        } catch (JSONException e) {
                            e.printStackTrace();
                            Toast.makeText(getApplicationContext(),
                                    "Error: " + e.getMessage(),
                                    Toast.LENGTH_LONG).show();
                        }

                        // hiding the progress dialog
                        hidepDialog();
                    }
                }, new Response.ErrorListener() {

                    @Override
                    public void onErrorResponse(VolleyError error) {
                        VolleyLog.d(TAG, "Error: " + error.getMessage());
                        Toast.makeText(getApplicationContext(),
                                error.getMessage(), Toast.LENGTH_SHORT).show();
                        // hide the progress dialog
                        hidepDialog();
                    }
                });

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

    /**
     * Verifying the mobile payment on the server to avoid fraudulent payment
     * */
    private void verifyPaymentOnServer(final String paymentId,
            final String payment_client) {
        // Showing progress dialog before making request
        pDialog.setMessage("Verifying payment...");
        showpDialog();

        StringRequest verifyReq = new StringRequest(Method.POST,
                Config.URL_VERIFY_PAYMENT, new Response.Listener<String>() {

                    @Override
                    public void onResponse(String response) {
                        Log.d(TAG, "verify payment: " + response.toString());

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

                            // user error boolean flag to check for errors

                            Toast.makeText(getApplicationContext(), message,
                                    Toast.LENGTH_SHORT).show();

                            if (!error) {
                                // empty the cart
                                productsInCart.clear();
                            }

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

                        // hiding the progress dialog
                        hidepDialog();

                    }
                }, new Response.ErrorListener() {

                    @Override
                    public void onErrorResponse(VolleyError error) {
                        Log.e(TAG, "Verify Error: " + error.getMessage());
                        Toast.makeText(getApplicationContext(),
                                error.getMessage(), Toast.LENGTH_SHORT).show();
                        // hiding the progress dialog
                        hidepDialog();
                    }
                }) {

            @Override
            protected Map<String, String> getParams() {

                Map<String, String> params = new HashMap<String, String>();
                params.put("paymentId", paymentId);
                params.put("paymentClientJson", payment_client);

                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 prepareFinalCart() {

        PayPalItem[] items = new PayPalItem[productsInCart.size()];
        items = productsInCart.toArray(items);

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

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

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

        PayPalPaymentDetails paymentDetails = new PayPalPaymentDetails(
                shipping, subtotal, tax);

        BigDecimal amount = subtotal.add(shipping).add(tax);

        PayPalPayment payment = new PayPalPayment(
                amount,
                Config.DEFAULT_CURRENCY,
                "Description about transaction. This will be displayed to the user.",
                Config.PAYMENT_INTENT);

        payment.items(items).paymentDetails(paymentDetails);

        // Custom field like invoice_number etc.,
        payment.custom("This is text that will be associated with the payment that the app can use.");

        return payment;
    }

    /**
     * Launching PalPay payment activity to complete the payment
     * */
    private void launchPayPalPayment() {

        PayPalPayment thingsToBuy = prepareFinalCart();

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

        intent.putExtra(PayPalService.EXTRA_PAYPAL_CONFIGURATION, paypalConfig);

        intent.putExtra(PaymentActivity.EXTRA_PAYMENT, thingsToBuy);

        startActivityForResult(intent, REQUEST_CODE_PAYMENT);
    }

    /**
     * Receiving the PalPay payment response
     * */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == REQUEST_CODE_PAYMENT) {
            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 paymentId = confirm.toJSONObject()
                                .getJSONObject("response").getString("id");

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

                        Log.e(TAG, "paymentId: " + paymentId
                                + ", payment_json: " + payment_client);

                        // Now verify the payment on the server side
                        verifyPaymentOnServer(paymentId, payment_client);

                    } catch (JSONException e) {
                        Log.e(TAG, "an extremely unlikely failure occurred: ",
                                e);
                    }
                }
            } else if (resultCode == Activity.RESULT_CANCELED) {
                Log.e(TAG, "The user canceled.");
            } else if (resultCode == PaymentActivity.RESULT_EXTRAS_INVALID) {
                Log.e(TAG,
                        "An invalid Payment or PayPalConfiguration was submitted.");
            }
        }
    }

    private void showpDialog() {
        if (!pDialog.isShowing())
            pDialog.show();
    }

    private void hidepDialog() {
        if (pDialog.isShowing())
            pDialog.dismiss();
    }

    @Override
    public void onAddToCartPressed(Product product) {

        PayPalItem item = new PayPalItem(product.getName(), 1,
                product.getPrice(), Config.DEFAULT_CURRENCY, product.getSku());

        productsInCart.add(item);

        Toast.makeText(getApplicationContext(),
                item.getName() + " added to cart!", Toast.LENGTH_SHORT).show();

    }

}

Như vậy chúng ta đã hoàn thành phần cài đặt cho Android. Để test chúng ta sẽ test đồng thời cả client và server trong môi trường sandbox theo từng bước dưới đây.

11. Kiểm tra các ứng dụng

Hãy chắc chắn rằng cả hai thiết bị (thiết bị chạy dự án php và các thiết bị Android) được kết nối với cùng mạng wifi.

Thay PAYPAL_CLIENT_ID & PAYPAL_SECRET trong file config.php với các khóa paypal của bạn trong dự án php.

Thay PAYPAL_CLIENT_ID trong Config.java với client id paypal của bạn

Run server WAMP và có được địa chỉ IP của máy tính bằng cách thực hiện lệnh ipconfig trong cmd. (Trên mac, sử dụng ifconfig để có được địa chỉ IP). Chúng tôi cần phải sử dụng địa chỉ IP này trong url thay vì localhost.

Thay thế địa chỉ IP của URL_PRODUCTS và URL_VERIFY_PAYMENT với địa chỉ ip của bạn trong Config.java.

Triển khai và chạy các ứng dụng Android trên thiết bị. Nếu bạn nhận được danh sách sản phẩm trưng bày, ứng dụng của bạn kết nối thành công với máy chủ php.

Hãy thử chọn các sản phẩm bạn muốn mua và tiến hành thanh toán. Bạn nên có thể nhìn thấy màn hình thanh toán PayPal và làm việc thanh toán. Khi nó yêu cầu thông tin paypal, sử dụng các thông tin sandbox.

danh sách các giỏ mua hàng paypal android cổng thanh toán paypal android

1

2

3

Như Vậy chúng ta đã hoàn thành việc tích hợp cổng thanh toán Paypal vào trong ứng dụng Android;

Tài liệu tham khảo:

Truy cập PayPal API REST doc để đọc chi tiết về các Paypal REST API.

PayPal Android Mobile SDK

PayPal PHP REST API SDK

Server image used in the illustration.


All Rights Reserved