Android集成PayPal支付

前言

由于公司业务需求,需要集成PayPal支付,今天特意去研究了一下,现在将自己的一些心得分享给大家

第一步

注册PayPal开发者账号,并且添加你的APP,这里大家可以去PayPal开发者平台去注册自己的账号 传送门
具体步骤:

  1. 打开PayPal开发者平台,点击右上角的Log into Dashboard按钮注册开发者账号,如图


    image.png

    如果已经有开发者账号了,直接登录即可,没有的话,自行注册

第二步

创建应用程序,获取所需要的Client ID


image

然后点击左边侧边栏的Accounts创建一个沙盒帐号测试用.创建页面我就不演示了,那个页面你随便填.只要你记住帐号密码就成.忘了就修改,不然就删掉重建.

第三步

在自己的项目工程中添加相应的SDK依赖,以Android Studio为例

在你工程Module的bulid.gradle里面添加如下依赖

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

这里附上附上PayPal sdk的github地址传送门,有需要的可以导入最新的依赖包
注意

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


示意图

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

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

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

通过以上两步,PayPal sdk就集成完成了,这里注意一下,PayPal sdk不需要在AndroidManifest.xml声明任何东西,直接使用就行了

注意坑 PayPal sdk需要okio,okhttp版本在3.0以上

代码逻辑

  1. 首先配置PayPalConfiguration,设置支付环境environment和clientId
  2. 调起支付前先启动PayPalService服务
  3. 创建支付对象PayPalPayment,使用startActivityForResult启动PaymentActivity跳转到PayPal界面完成支付,在onActivityResult方法中接收处理结果并获取PaymentConfirmation对象
  4. 收到结果后安卓端将PaymentConfirmation对象中的结果id传递给服务端,服务端调用PayPal查询接口查询支付结果

这里给大家提供一个工具类,已经封装好了,直接拿去用,这里说下使用方法,只需要在相应的声明周期里调用即可,非常的方便简单

  1. 在你初始化的方法里面调起支付服务
PayPalHelper.getInstance().stopPayPalService(this);
示意图
  1. 在需要调起支付的地方调起支付
PayPalHelper.getInstance().doPayPalPay(mContext);
  1. 在类的onActivityResult 里进行回调结果的处理,直接上代码
 @Override
  protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    PayPalHelper.getInstance().confirmPayResult(mContext, requestCode, resultCode, data, new PayPalHelper.DoResult() {
        @Override
        public void confirmSuccess(String id) {
            showShortToast("支付成功" + id);
        }

        @Override
        public void confirmNetWorkError() {
            showShortToast("支付失败");
        }

        @Override
        public void customerCanceled() {
            showShortToast("支付取消");
        }

        @Override
        public void confirmFuturePayment() {

        }

        @Override
        public void invalidPaymentConfiguration() {

        }
    });
}
  1. 在类的onDestroy()方法里注销服务
PayPalHelper.getInstance().startPayPalService(mContext);
示意图

经过以上4步,你便可以进行PayPal支付了,是不是很简单.

PayPalHelper工具类

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");
                            //根据Id从自己的服务器判断相应的查询逻辑
                            doResult.confirmSuccess(id);
                        }
                    }
                } 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(String id);

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

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

    //授权支付
    void confirmFuturePayment();

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

}

集成PayPal支付传送门,里面有更详细的的教程,大家可以去看看

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

推荐阅读更多精彩内容

  • 由于最近公司业务需求,为了提升用户体验,产品决定把原本直接加载M版的支付方式,直接改成调起PayPal SDK的方...
    Moosen阅读 10,322评论 22 25
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,378评论 25 707
  • 此项目已开源 赶快来围观 Start支持下吧 【客户端开源地址-JPay】【服务端端开源地址-在com.javen...
    LucasAdam阅读 1,929评论 0 3
  • Git https://github.com/paypal/PayPal-Android-SDK/blob/mas...
    乘风破浪的程序员阅读 1,953评论 0 3
  • “谁人与汝立黄昏,谁人问我粥可温。” 1 众所周知,古时候的男婚女嫁,省事又省心,几乎都不用恋爱,甚至人儿都可以不...
    沈万九阅读 22,158评论 166 350