Android 集成PayPal支付

由于最近公司业务需求,为了提升用户体验,产品决定把原本直接加载M版的支付方式,直接改成调起PayPal SDK的方式进行支付,从而可以大大提升加载速度,改善体验。虽然后来出于支付安全考虑否决了这个决定,但是在这里还是跟大家来分享一下集成Android的集成PayPal支付,同时简单封装了一下支付的帮助类,希望能帮到大家。
<h4>
1.注册PayPal开发者账号,并且添加你的APP
</h4>
首先我们得去到PayPal的开发者平台去注册自己的账号 https://developer.paypal.com ,同时创建自己的APP,获取APP的CLient ID.


<h4>
2.在自己的工程里添加相应的SDK依赖
</h4>

把下面一句代码添加到你工程的gradle.bulid里面

compile('com.paypal.sdk:paypal-android-sdk:2.15.1') 
{ exclude group: 'io.card' }//禁止通过信用卡直接支付,如果不禁止可以直接去掉这一句

<h4>
2.强制在你的编译版本上执行编译操作(编译不过的时候才建议添加)
</h4>

由于PayPal sdk 现在已经升级到了默认最低是minSDKVersion 16 ,所以如果你的编译版本低于这个版本的时候,AS就会提示你编译不通过,并且报错

Error:Execution failed for task ':app:processDebugManifest'.
> Manifest merger failed : uses-sdk:minSdkVersion 14 cannot be smaller than version 16 declared in library [com.paypal.sdk:paypal-android-sdk:2.15.1] /home/moosen/Documents/GearBest/app/build/intermediates/exploded-aar/com.paypal.sdk/paypal-android-sdk/2.15.1/AndroidManifest.xml
Suggestion: use tools:overrideLibrary="com.paypal.android.sdk.payments" to force usage

这时你需要在清单文件AndroidManifest.xml里面添加一下代码强制它在你需要的版本下编译

<uses-sdk    android:minSdkVersion="这里填写你需要的编译版本"    tools:overrideLibrary="com.paypal.android.sdk.payments" />

当然你也可以把你的minSDKVersion改到16或者更大

<h4>
3.在你需要调起支付的页面配置支付环境(或者在基类配置相应的支付环境)
</h4>

//配置何种支付环境,一般沙盒,正式    
private static final String CONFIG_ENVIRONMENT = PayPalConfiguration.ENVIRONMENT_SANDBOX;    
// note that these credentials will differ between live & sandbox environments.    
//你所注册的APP Id    private static final String CONFIG_CLIENT_ID = "你所注册的CLient Id";   
private static final int REQUEST_CODE_PAYMENT = 1;    
private static final int REQUEST_CODE_FUTURE_PAYMENT = 2;    
private static final int REQUEST_CODE_PROFILE_SHARING = 3;    
private static PayPalConfiguration config = new PayPalConfiguration()            .environment(CONFIG_ENVIRONMENT)            
.clientId(CONFIG_CLIENT_ID);    
//以下配置是授权支付的时候用到的
//.merchantName("Example Merchant")
// .merchantPrivacyPolicyUri(Uri.parse("https://www.example.com/privacy"))
//.merchantUserAgreementUri(Uri.parse("https://www.example.com/legal"));

<h4>
4.在类的onCreate方法里面调起支付服务
</h4>

Intent intent = new Intent(this, PayPalService.class);
intent.putExtra(PayPalService.EXTRA_PAYPAL_CONFIGURATION, config);
startService(intent);

<h4>
5.创建支付实体,在需要调起支付的地方调起支付
</h4>

public void onBuyPressed(View pressed) {
//创建支付对象,用于传过去给PayPal服务器进行收款
PayPalPayment thingToBuy = getThingToBuy(PayPalPayment.PAYMENT_INTENT_SALE);
Intent intent = new Intent(SampleActivity.this, PaymentActivity.class);
intent.putExtra(PayPalService.EXTRA_PAYPAL_CONFIGURATION, config);
intent.putExtra(PaymentActivity.EXTRA_PAYMENT, thingToBuy);
//这里直接调起PayPal的sdk进行付款操作
startActivityForResult(intent, REQUEST_CODE_PAYMENT);
}
//这里只传一个总价格或者单个产品的信息收款情况
private PayPalPayment getThingToBuy(String paymentIntent) {
return new PayPalPayment(new BigDecimal("0.01"), "USD", "sample item",
paymentIntent);

}
//这里是购买一系列产品创建购买对象
private PayPalPayment getStuffToBuy(String paymentIntent) {
PayPalItem[] items =
{
new PayPalItem("sample item #1", 2, new BigDecimal("87.50"), "USD",
"sku-12345678"),
new PayPalItem("free sample item #2", 1, new BigDecimal("0.00"),
"USD", "sku-zero-price"),
new PayPalItem("sample item #3 with a longer name", 6, new BigDecimal("37.99"),
"USD", "sku-33333")
};
BigDecimal subtotal = PayPalItem.getItemTotal(items);
BigDecimal shipping = new BigDecimal("7.21");
BigDecimal tax = new BigDecimal("4.67");
PayPalPaymentDetails paymentDetails = new PayPalPaymentDetails(shipping, subtotal, tax);
BigDecimal amount = subtotal.add(shipping).add(tax);
PayPalPayment payment = new PayPalPayment(amount, "USD", "sample item", paymentIntent);
payment.items(items).paymentDetails(paymentDetails);
//--- set other optional fields like invoice_number, custom field, and soft_descriptor
payment.custom("This is text that will be associated with the payment that the app can use.");
return payment;
}

<h4>
6.在类的onActivityResult 里进行回调结果的处理
</h4>

if (resultCode == Activity.RESULT_OK) {
PaymentConfirmation confirm =
data.getParcelableExtra(PaymentActivity.EXTRA_RESULT_CONFIRMATION);
if (confirm != null) {
try {
Log.i(TAG, confirm.toJSONObject().toString(4));
Log.i(TAG, confirm.getPayment().toJSONObject().toString(4));
//这里可以把PayPal带回来的json数据传给服务器以确认你的款项是否收到或者收全
//可以直接把 confirm.toJSONObject() 这个带给服务器,
//得到服务器返回的结果,你就可以跳转成功页面或者做相应的处理了
} catch (JSONException e) {
Log.e(TAG, "an extremely unlikely failure occurred: ", e);
}
}
} else if (resultCode == Activity.RESULT_CANCELED) {
Log.i(TAG, "The user canceled.");
} else if (resultCode == PaymentActivity.RESULT_EXTRAS_INVALID) {
Log.i(
TAG,
"An invalid Payment or PayPalConfiguration was submitted. Please see the docs.");
}
} 

这里授权支付就不做解析和分析了

<h4>
7.在类的onDestroy 注销服务
</h4>

stopService(new Intent(this, PayPalService.class));

<h4>
7.最后,这里简单的封装了一个PayPalHelper帮助类
</h4>
你只需要在相应的生命周期里面调用PayPalHelper里面的方法,就可以做相应的处理

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

import com.globalegrow.app.gearbest.network.HttpCallBack;
import com.globalegrow.app.gearbest.widget.CustomToast;
import com.paypal.android.sdk.payments.PayPalAuthorization;
import com.paypal.android.sdk.payments.PayPalConfiguration;
import com.paypal.android.sdk.payments.PayPalFuturePaymentActivity;
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.PayPalProfileSharingActivity;
import com.paypal.android.sdk.payments.PayPalService;
import com.paypal.android.sdk.payments.PaymentActivity;
import com.paypal.android.sdk.payments.PaymentConfirmation;

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

import java.io.IOException;
import java.math.BigDecimal;

/**
 * Create by Moosen on 09/11/2016
 */
public class PayPalHelper {

    private static final String TAG = "PayPalHelper";
    //配置何种支付环境,一般沙盒,正式
    private static final String CONFIG_ENVIRONMENT = PayPalConfiguration.ENVIRONMENT_SANDBOX;

    // note that these credentials will differ between live & sandbox environments.
    //你所注册的APP Id
    private static final String CONFIG_CLIENT_ID = "";

    private static final int REQUEST_CODE_PAYMENT = 1;
    private static final int REQUEST_CODE_FUTURE_PAYMENT = 2;
    private static final int REQUEST_CODE_PROFILE_SHARING = 3;

    private static PayPalConfiguration config = new PayPalConfiguration()
            .environment(CONFIG_ENVIRONMENT)
            .clientId(CONFIG_CLIENT_ID);
    //以下配置是授权支付的时候用到的
//            .merchantName("Example Merchant")
//            .merchantPrivacyPolicyUri(Uri.parse("https://www.example.com/privacy"))
//            .merchantUserAgreementUri(Uri.parse("https://www.example.com/legal"));
    private static PayPalHelper payPalHelper;

    private PayPalHelper() {
    }

    public static PayPalHelper getInstance() {
        if (payPalHelper == null) {
            synchronized (PayPalHelper.class) {
                payPalHelper = new PayPalHelper();
            }
        }
        return payPalHelper;
    }

    /**
     * 启动PayPal服务
     *
     * @param context
     */
    public void startPayPalService(Context context) {
        Intent intent = new Intent(context, PayPalService.class);
        intent.putExtra(PayPalService.EXTRA_PAYPAL_CONFIGURATION, config);
        context.startService(intent);
    }

    /**
     * 停止PayPal服务  sdfsdfsdssaaass
     *
     * @param context
     */
    public void stopPayPalService(Context context) {
        context.stopService(new Intent(context, PayPalService.class));
    }

    /**
     * 开始执行支付操作
     *
     * @param context
     */
    public void doPayPalPay(Context context) {
        /*
         * PAYMENT_INTENT_SALE will cause the payment to complete immediately.
         * Change PAYMENT_INTENT_SALE to
         *   - PAYMENT_INTENT_AUTHORIZE to only authorize payment and capture funds later.
         *   - PAYMENT_INTENT_ORDER to create a payment for authorization and capture
         *     later via calls from your server.
         *
         * Also, to include additional payment details and an item list, see getStuffToBuy() below.
         */
        PayPalPayment thingToBuy = getStuffToBuy(PayPalPayment.PAYMENT_INTENT_SALE);
        /*
         * See getStuffToBuy(..) for examples of some available payment options.
         */
        Intent intent = new Intent(context, PaymentActivity.class);

        // send the same configuration for restart resiliency
        intent.putExtra(PayPalService.EXTRA_PAYPAL_CONFIGURATION, config);

        intent.putExtra(PaymentActivity.EXTRA_PAYMENT, thingToBuy);

        ((Activity) context).startActivityForResult(intent, REQUEST_CODE_PAYMENT);
    }

    /*
         * This method shows use of optional payment details and item list.
         *
         * 直接给PP创建支付的信息,支付对象实体信息
         */
    private PayPalPayment getStuffToBuy(String paymentIntent) {
        //--- include an item list, payment amount details
        //具体的产品信息列表
        PayPalItem[] items =
                {
                        new PayPalItem("sample item #1", 2, new BigDecimal("0.50"), "USD",
                                "sku-12345678"),
                        new PayPalItem("free sample item #2", 1, new BigDecimal("0.00"),
                                "USD", "sku-zero-price"),
                        new PayPalItem("sample item #3 with a longer name", 6, new BigDecimal("0.99"),
                                "USD", "sku-33333")
                };
        BigDecimal subtotal = PayPalItem.getItemTotal(items);
        BigDecimal shipping = new BigDecimal("0.21");
        BigDecimal tax = new BigDecimal("0.67");
        PayPalPaymentDetails paymentDetails = new PayPalPaymentDetails(shipping, subtotal, tax);
        BigDecimal amount = subtotal.add(shipping).add(tax);
        PayPalPayment payment = new PayPalPayment(amount, "USD", "sample item", paymentIntent);
        payment.items(items).paymentDetails(paymentDetails);
        //--- set other optional fields like invoice_number, custom field, and soft_descriptor
        payment.custom("This is text that will be associated with the payment that the app can use.");
        return payment;
    }

    /**
     * 处理支付之后的结果
     *
     * @param context
     * @param requestCode
     * @param resultCode
     * @param data
     */
    public void confirmPayResult(final Context context, int requestCode, int resultCode, Intent data, final DoResult doResult) {
        if (requestCode == REQUEST_CODE_PAYMENT) {
            if (resultCode == Activity.RESULT_OK) {
                PaymentConfirmation confirm =
                        data.getParcelableExtra(PaymentActivity.EXTRA_RESULT_CONFIRMATION);
                if (confirm != null) {
                    try {
                        Log.i(TAG, confirm.toJSONObject().toString(4));
                        Log.i(TAG, confirm.getPayment().toJSONObject().toString(4));

                        /**
                         *  TODO: send 'confirm' (and possibly confirm.getPayment() to your server for verification
                         * or consent completion.
                         * See https://developer.paypal.com/webapps/developer/docs/integration/mobile/verify-mobile-payment/
                         * for more details.
                         *
                         * For sample mobile backend interactions, see
                         * https://github.com/paypal/rest-api-sdk-python/tree/master/samples/mobile_backend
                         */
//                        displayResultText("PaymentConfirmation info received from PayPal");
                        // 这里直接跟服务器确认支付结果,支付结果确认后回调处理结果
                        JSONObject jsonObject = confirm.toJSONObject();
                        if (jsonObject != null) {
                            JSONObject response = jsonObject.optJSONObject("response");
                            if (response != null) {
                                String id = response.optString("id");
                                try {
                                    CartManager.getInstance().confirmPayPalPayPrice(context, id, new HttpCallBack<String>() {
                                        @Override
                                        public void onSuccess(String responseString) {
                                            if (responseString != null) {
                                                try {
                                                    JSONObject jsonObject = new JSONObject(responseString);
                                                    if (jsonObject != null) {
                                                        int resultcode = jsonObject.optInt("_resultcode");
                                                        String msg = jsonObject.optString("_msg");
                                                        if (200 == resultcode) {
                                                            doResult.confirmSuccess();
                                                            Log.i(TAG, "dddddddd");
                                                        } else {
                                                            Log.i(TAG, "ssssssss");
                                                            CustomToast.getInstance(context).showToast(msg);
                                                            doResult.confirmNetWorkError();
                                                        }
                                                        CustomToast.getInstance(context).showToast(msg);
                                                    }
                                                } catch (JSONException e) {
                                                    e.printStackTrace();
                                                    doResult.confirmNetWorkError();
                                                }
                                            }
                                        }

                                        @Override
                                        public void onFailure(IOException e) {
                                            Log.i(TAG, "aaaaaaaa");
                                            doResult.confirmNetWorkError();
                                        }
                                    });
                                } catch (Exception e) {
                                    e.printStackTrace();
                                    doResult.confirmNetWorkError();
                                }
                            }
                        }
                    } catch (JSONException e) {
                        Log.e(TAG, "an extremely unlikely failure occurred: ", e);
                        doResult.confirmNetWorkError();
                    }
                }
            } else if (resultCode == Activity.RESULT_CANCELED) {
                Log.i(TAG, "The user canceled.");
                doResult.customerCanceled();
            } else if (resultCode == PaymentActivity.RESULT_EXTRAS_INVALID) {
                doResult.invalidPaymentConfiguration();
                Log.i(
                        TAG,
                        "An invalid Payment or PayPalConfiguration was submitted. Please see the docs.");
            }
        } else if (requestCode == REQUEST_CODE_FUTURE_PAYMENT) {
            if (resultCode == Activity.RESULT_OK) {
                PayPalAuthorization auth =
                        data.getParcelableExtra(PayPalFuturePaymentActivity.EXTRA_RESULT_AUTHORIZATION);
                if (auth != null) {
                    try {
                        doResult.confirmFuturePayment();
                        Log.i("FuturePaymentExample", auth.toJSONObject().toString(4));

                        String authorization_code = auth.getAuthorizationCode();
                        Log.i("FuturePaymentExample", authorization_code);

//                        sendAuthorizationToServer(auth);
//                        displayResultText("Future Payment code received from PayPal");

                    } catch (JSONException e) {
                        doResult.confirmNetWorkError();
                        Log.e("FuturePaymentExample", "an extremely unlikely failure occurred: ", e);
                    }
                }
            } else if (resultCode == Activity.RESULT_CANCELED) {
                Log.i("FuturePaymentExample", "The user canceled.");
                doResult.customerCanceled();
            } else if (resultCode == PayPalFuturePaymentActivity.RESULT_EXTRAS_INVALID) {
                doResult.invalidPaymentConfiguration();
                Log.i(
                        "FuturePaymentExample",
                        "Probably the attempt to previously start the PayPalService had an invalid PayPalConfiguration. Please see the docs.");
            }
        } else if (requestCode == REQUEST_CODE_PROFILE_SHARING) {
            if (resultCode == Activity.RESULT_OK) {
                PayPalAuthorization auth =
                        data.getParcelableExtra(PayPalProfileSharingActivity.EXTRA_RESULT_AUTHORIZATION);
                if (auth != null) {
                    try {
                        Log.i("ProfileSharingExample", auth.toJSONObject().toString(4));

                        String authorization_code = auth.getAuthorizationCode();
                        Log.i("ProfileSharingExample", authorization_code);

//                        sendAuthorizationToServer(auth);
//                        displayResultText("Profile Sharing code received from PayPal");

                    } catch (JSONException e) {
                        Log.e("ProfileSharingExample", "an extremely unlikely failure occurred: ", e);
                    }
                }
            } else if (resultCode == Activity.RESULT_CANCELED) {
                Log.i("ProfileSharingExample", "The user canceled.");
            } else if (resultCode == PayPalFuturePaymentActivity.RESULT_EXTRAS_INVALID) {
                Log.i(
                        "ProfileSharingExample",
                        "Probably the attempt to previously start the PayPalService had an invalid PayPalConfiguration. Please see the docs.");
            }
        }
    }

    /**
     * c处理完结果之后回调
     */
    public interface DoResult {
        //与服务确认支付成功
        void confirmSuccess();

        //网络异常或者json返回有问题
        void confirmNetWorkError();

        //用户取消支付
        void customerCanceled();

        //授权支付
        void confirmFuturePayment();

        //订单支付验证无效
        void invalidPaymentConfiguration();
    }

}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,047评论 25 707
  • 实现支付宝支付的准备工作: 1.向支付宝签约,成为支付宝的商户 签约完成后,支付宝会提供一些必要的数据给我们 商户...
    Anson杨春安阅读 8,164评论 0 6
  • 8 管理支付和订单 在上一章中,你创建了一个包括商品目录和订单系统的在线商店。你还学习了如何用Celery启动异步...
    lakerszhy阅读 2,333评论 1 4
  • 2016年的第一天,我在布达佩斯寻找一家很著名的鱼餐厅。 我从住处出发,穿过繁华的佩斯,穿过长长的链子桥,到达了多...
    Frau_R阅读 496评论 3 1
  • “宝贝,妈妈给你说件事。明天我和小姑娘约好了,她姐姐带着她我带着你我们一起去吃个饭,行吗?”刚刚回家的我屁股还没有...
    姬磊阅读 473评论 0 2