一文看懂主流的Android移動支付

前言

相信做app的童鞋都知道雅潭,我們app 肯定離不開移動支付的均蜜,而我們經(jīng)常使用的支付一般就是微信支付,支付寶支付這2個示绊。
而題主最近剛好離職锭部,也比較閑,剛好總結(jié)了之前做過的支付DEMO面褐,整合了微信支付拌禾,支付寶支付,銀聯(lián)支付展哭,貝寶支付湃窍,整合成一個demo,也算是一個經(jīng)驗總結(jié)吧匪傍。


丑丑的界面

github地址:https://github.com/LinHuanTanLy/Pay_Master

正文

項目結(jié)構(gòu)介紹:

最基礎的項目目錄結(jié)構(gòu)

其中網(wǎng)絡請求我們使用了okhttputils您市,解析JSON用了gson

配置類介紹:

我們所有需要的配置都寫在配置類里面,這個類也在我們項目中扮演著很重要的角色役衡,當然茵休,為了避嫌,我們BASE_URL以及一些配置文件做了xxxxx處理手蝎,大家可以替換成你們自己的key然后再運行榕莺。代碼如下:


/**
 * @author Ly
 * @date 2017/10/24
 * 配置類
 */

public class AppConf {
    /**
     * BaseUrl
     */
    private static final String BASE_URL = "https://app.globalxxx.com";

    /**
     * 微信配置文件
     */
    public interface WechatConf {
        String APP_ID = "xxxxxxxab57ab288";
        String APP_SECRET = "xxxxxxxx1269874371fc75a79a";
    }

    /**
     * 貝寶的配置文件
     */
    public interface PayPalConf {
        /**
         * 沙盒環(huán)境
         */
        String CONFIG_CLIENT_ID_SANDBOX = "xxxxlZPMz2PN3C8akROwHZjDnzmRDGdxt965BkkvVfF8cUlzRU8a2AALVYCPMSc9uwqyJY5";
        /**
         * 正式環(huán)境
         */
        String CONFIG_CLIENT_ID_LIVE = "xxxxEiCmYlq83H73jHejGw8r-hyaiL0WbueHIUbOLtSEV_Vfh8rkU4aawFknGQZhA";
    }

    /**
     * 銀行卡配置環(huán)境
     */
    public interface CardConf {
        /**
         * 銀聯(lián)支付 配置信息
         *
         * @return 01 沙盒環(huán)境 00 正式環(huán)境
         */
        String SANDBOX = "01", FORMAL = "00";

    }

    /**
     * 接口配置文件
     */
    public interface NetConf {
        /**
         * 登錄信息
         */
        String LOGIN = BASE_URL + "/user/login.do";
        /**
         * 微信支付信息
         */
        String WECHAT_PAY_INFO = BASE_URL + "/user/pay/weChatRecharge.do";
        /**
         * 支付寶支付信息
         */
        String ALI_PAY_INFO = BASE_URL + "/user/pay/alipayRecharge.do";

        /**
         * 貝寶支付信息
         */
        String PAYPAL_PAY_INFO = BASE_URL + "/user/pay/getPayPalOrderId.do";
        /**
         * 驗證paypal支付狀態(tài)
         */
        String PAYPAL_SYN_ORDER = BASE_URL + "/user/pay/synPaypalOrder.do";
        /**
         * 銀聯(lián)支付 獲取tn
         */
        String CARD_PAY_INFO = "http://101.231.204.84:8091/sim/getacptn";
    }
}

微信支付:

微信支付現(xiàn)在已經(jīng)可以支持依賴導入了。

  1. 在build.gradle 寫入依賴:
compile 'com.tencent.mm.opensdk:wechat-sdk-android-without-mta:+'
  1. 在清單文件中寫入權限:

 <!--for wechat pay-->
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

  1. 在后臺配置的包名下新建wxapi包棵介,并且新建一個類钉鸯,命名為WXPayEntryActivity,實現(xiàn)IWXAPIEventHandler接口邮辽,其中我們需要注意的是:假設項目包名為 com.ly.PayDemo唠雕,那么這個類的路徑就應該是com.ly.PayDemo.wxapi.WXPayEntryActivity扣蜻,包名,路徑都不可以錯及塘,否則無法進入回調(diào)頁面!并且記得在AndroidManifest.xml進行注冊锐极。
  <!--微信支付成功回調(diào)的頁面-->
        <activity
            android:name=".wxapi.WXPayEntryActivity"
            android:exported="true"
            android:launchMode="singleTop"/>
        <!--end of 微信支付-->

  1. 真正進行微信支付笙僚,首先我們看下我們的支付類,相信大家可以看出邏輯:
微信支付頁面
/**
 * @author Ly
 * @date 2017/10/24
 * 微信支付界面
 */

public class WeChatPayActivity extends AppCompatActivity implements View.OnClickListener {


    private TextView mTvWechatPay, mTvWechatIsSupport;


    /**
     * 微信支付
     */
    private IWXAPI api;

    private String token;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_wechat_pay);
        Bundle bundle = getIntent().getBundleExtra("extra");
        if (bundle != null) {
            token = bundle.getString("token");
        }
        initView();
    }

    private void initView() {
        mTvWechatPay = (TextView) findViewById(R.id.tv_wechat_pay);
        mTvWechatIsSupport = (TextView) findViewById(R.id.tv_wechat_is_support);
        mTvWechatPay.setOnClickListener(this);
        mTvWechatIsSupport.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.tv_wechat_pay:
                doGetWechatPayInfo();
                break;
            case R.id.tv_wechat_is_support:
                isPaySupported();
                break;
            default:
                break;
        }
    }

    /**
     * 獲取微信訂單
     */
    private void doGetWechatPayInfo() {
        OkHttpUtils.post()
                .url(AppConf.NetConf.WECHAT_PAY_INFO)
                .addParams("accessToken", token)
                .addParams("wRechargeMoney", "0.01")
                .build()
                .execute(new StringCallback() {
                    @Override
                    public void onError(Call call, Exception e, int id) {
                        e.printStackTrace();
                    }

                    @Override
                    public void onResponse(String response, int id) {
                        WechatPayInfo wechatPayInfo = new Gson().fromJson(response, WechatPayInfo.class);
                        Log.e("xxx", wechatPayInfo.toString());
                        WXPay(wechatPayInfo.getData().getAppid(),
                                wechatPayInfo.getData().getPartnerid(),
                                wechatPayInfo.getData().getPrepayid(),
                                wechatPayInfo.getData().getTimestamp(),
                                wechatPayInfo.getData().getNoncestr(),
                                wechatPayInfo.getData().getPaySign());
                    }
                });
    }

    /**
     * 微信支付
     */
    private void WXPay(String appId, String partnerId, String prepayId, String timeStamp, String nonceStr, String paySign) {
        api = WXAPIFactory.createWXAPI(this, null);
        api.registerApp(AppConf.WechatConf.APP_ID);
        PayReq req = new PayReq();
        req.appId = appId;
        req.partnerId = partnerId;
        req.prepayId = prepayId;
        req.nonceStr = nonceStr;
        req.timeStamp = timeStamp;
        req.packageValue = "Sign=WXPay";
        req.sign = paySign;
        api.sendReq(req);
    }

    /**
     * 檢測微信版本是否支持支付
     */
    private boolean isPaySupported() {
        if (api == null) {
            api = WXAPIFactory.createWXAPI(this, null);
            api.registerApp(AppConf.WechatConf.APP_ID);
        }
        boolean isPaySupported;
        isPaySupported = api.getWXAppSupportAPI() >= Build.PAY_SUPPORTED_SDK_INT;
        if (!isPaySupported) {
            Toast.makeText(this, "您的微信版本不支持支付", Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(this, "您的微信版本支持支付", Toast.LENGTH_SHORT).show();
        }
        return isPaySupported;
    }

    public static void toWechatActivity(Activity activity, String token) {
        Intent intent = new Intent(activity, WeChatPayActivity.class);
        Bundle bundle = new Bundle();
        bundle.putString("token", token);
        intent.putExtra("extra", bundle);
        activity.startActivity(intent);
    }
}


我們可以看到灵再,其實就分為2個方法:

  • isPaySupported 檢測是否有微信客戶端以及是否該客戶端支持微信支付
  • doGetWechatPayInfo 從服務器獲取訂單信息肋层,在callback里面吊起微信支付。
  1. 值得一提的是翎迁,我們要如何接受支付的回調(diào)栋猖,那就是要靠我們之前大費周章寫的WXPayEntryActivity:
 @Override
    public void onResp(BaseResp resp) {
        Log.e("", "onPayFinish, errCode = " + resp.errCode);
        int code = resp.errCode;
        switch (code) {
            case 0://支付成功后的界面
                Toast.makeText(this, "支付成功", Toast.LENGTH_SHORT).show();
                finish();
                break;
            case -1:
                Toast.makeText(this, "簽名錯誤、未注冊APPID汪榔、項目設置APPID不正確蒲拉、注冊的APPID與設置的不匹配、您的微信賬號異常等", Toast.LENGTH_SHORT).show();
                finish();
                break;
            case -2://用戶取消支付后的界面
                Toast.makeText(this, "用戶取消", Toast.LENGTH_SHORT).show();
                finish();
                break;
            default:
                break;
        }
    }

在這里我們可以處理下這個頁面痴腌,而如果我們不需要這個頁面而又必須靠這個頁面去接受回調(diào)雌团,有個小技巧,我們可以把這個頁面1dp士聪,或者是修改theme為dialog锦援。

支付寶支付:

  1. 導入JAR:
    支付寶的話暫時沒有依賴方法,我們需要手動導入jar包:


    支付寶的jar包
  2. 配置清單文件:
    需要配置的權限為:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

其實與微信支付是一樣的剥悟,所以我們不贅述灵寺。
但是我們要配置2個activity:

 <!--支付寶支付-->
        <activity
            android:name="com.alipay.sdk.app.H5PayActivity"
            android:configChanges="orientation|keyboardHidden|navigation|screenSize"
            android:exported="false"
            android:screenOrientation="behind"
            android:windowSoftInputMode="adjustResize|stateHidden">
        </activity>
        <activity
            android:name="com.alipay.sdk.app.H5AuthActivity"
            android:configChanges="orientation|keyboardHidden|navigation"
            android:exported="false"
            android:screenOrientation="behind"
            android:windowSoftInputMode="adjustResize|stateHidden">
        </activity>

        <!--end of  支付寶支付-->

  1. 我們看下支付寶支付的類:

/**
 * @author Ly
 * @date 2017/10/24
 * 支付寶支付
 */

public class AliPayActivity extends AppCompatActivity {
    private Button mBtAliPay;

    private String token;
    /**
     * 支付寶支付
     */
    private static final int SDK_PAY_FLAG = 1;


    @SuppressLint("HandlerLeak")
    private Handler mHandler = new Handler() {
        @Override
        @SuppressWarnings("unused")
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case SDK_PAY_FLAG: {
                    AliPayResult payResult = new AliPayResult((String) msg.obj);
                    /**
                     * 同步返回的結(jié)果必須放置到服務端進行驗證(驗證的規(guī)則請看https://doc.open.alipay.com/doc2/
                     * detail.htm?spm=0.0.0.0.xdvAU6&treeId=59&articleId=103665&
                     * docType=1) 建議商戶依賴異步通知
                     */
                    String resultInfo = payResult.getResult();// 同步返回需要驗證的信息
                    String resultStatus = payResult.getResultStatus();
                    // 判斷resultStatus 為“9000”則代表支付成功,具體狀態(tài)碼代表含義可參考接口文檔
                    if (TextUtils.equals(resultStatus, "9000")) {
                        Toast.makeText(AliPayActivity.this, "支付成功", Toast.LENGTH_SHORT).show();
                    } else {
                        // 判斷resultStatus 為非"9000"則代表可能支付失敗
                        // "8000"代表支付結(jié)果因為支付渠道原因或者系統(tǒng)原因還在等待支付結(jié)果確認区岗,最終交易是否成功以服務端異步通知為準(小概率狀態(tài))
                        if (TextUtils.equals(resultStatus, "8000")) {
                            Toast.makeText(AliPayActivity.this, "支付結(jié)果確認中", Toast.LENGTH_SHORT).show();

                        } else {
                            // 其他值就可以判斷為支付失敗略板,包括用戶主動取消支付,或者系統(tǒng)返回的錯誤
                            Toast.makeText(AliPayActivity.this, "支付失敗", Toast.LENGTH_SHORT).show();

                        }
                    }
                    break;
                }
                default:
                    break;
            }
        }
    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_ali_pay);
        Bundle bundle = getIntent().getBundleExtra("extra");
        if (bundle != null) {
            token = bundle.getString("token");
        }
        initView();
    }

    private void initView() {
        mBtAliPay = (Button) findViewById(R.id.bt_ali_pay);
        mBtAliPay.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                doGetAliPayInfo();
            }
        });
    }


    /**
     * 獲取支付寶信息
     */
    private void doGetAliPayInfo() {
        OkHttpUtils.post().url(AppConf.NetConf.ALI_PAY_INFO)
                .addParams("accessToken", token)
                .addParams("aRechargeMoney", "0.01")
                .build()
                .execute(new StringCallback() {
                    @Override
                    public void onError(Call call, Exception e, int id) {
                        e.printStackTrace();
                    }

                    @Override
                    public void onResponse(String response, int id) {
                        AliPayInfo aliPayInfo = new Gson().fromJson(response, AliPayInfo.class);
                        aliPay(aliPayInfo.getData().getOrderStr());
                    }
                });
    }

    /**
     * 調(diào)用SDK支付
     */
    public void aliPay(final String payInfo) {
        Runnable payRunnable = new Runnable() {
            @Override
            public void run() {
                // 構(gòu)造PayTask 對象
                PayTask alipay = new PayTask(AliPayActivity.this);
                // 調(diào)用支付接口慈缔,獲取支付結(jié)果
                String result = alipay.pay(payInfo, true);
                Message msg = new Message();
                msg.what = SDK_PAY_FLAG;
                msg.obj = result;
                mHandler.sendMessage(msg);
            }
        };
        // 必須異步調(diào)用
        Thread payThread = new Thread(payRunnable);
        payThread.start();
    }

    public static void toAliPayAcitivity(Activity activity, String token) {
        Intent intent = new Intent(activity, AliPayActivity.class);
        Bundle bundle = new Bundle();
        bundle.putString("token", token);
        intent.putExtra("extra", bundle);
        activity.startActivity(intent);
    }
}

其實我們這個類中只有一個重要的方法:

  • doGetAliPayInfo 從服務器獲取我們的訂單信息蚯根。
    獲取到了信息后,我們通過調(diào)用支付寶的支付api吊起支付:
 /**
     * 調(diào)用SDK支付
     */
    public void aliPay(final String payInfo) {
        Runnable payRunnable = new Runnable() {
            @Override
            public void run() {
                // 構(gòu)造PayTask 對象
                PayTask alipay = new PayTask(AliPayActivity.this);
                // 調(diào)用支付接口胀糜,獲取支付結(jié)果
                String result = alipay.pay(payInfo, true);
                Message msg = new Message();
                msg.what = SDK_PAY_FLAG;
                msg.obj = result;
                mHandler.sendMessage(msg);
            }
        };
        // 必須異步調(diào)用
        Thread payThread = new Thread(payRunnable);
        payThread.start();
    }

而吊起了支付后颅拦,支付結(jié)果我們可以在handler中看到,當然教藻!涉及到支付的距帅,我們app本地所謂“支付成功”,"支付失敗"都是不靠譜的,最靠譜的還是得靠服務器那邊的回調(diào)括堤。

銀聯(lián)支付

  1. 導入JAR以及SO
    銀聯(lián)的導入相對比較麻煩碌秸,需要導入JAR,SO,還有BIN文件
    銀聯(lián)支付的配置

    因為我們的so是放在libs下面的绍移,并且我們只導入3個cpu類型,所以我們需要在build.gradle文件做如下配置:
    defaultConfig下配置ndk:
ndk {
            //選擇要添加的對應cpu類型的.so庫讥电。
            abiFilters 'armeabi', 'armeabi-v7a', 'arm64-v8a'
            // 還可以添加 'x86', 'x86_64', 'mips', 'mips64'
        }

android下配置:

  sourceSets {
        main {
            jniLibs.srcDirs = ['libs']
        }
    }

至此導入完成蹂窖。

  1. 我們?nèi)耘f需要在清單文件進行配置:

  <!--銀聯(lián)支付-->
        <uses-library
            android:name="org.simalliance.openmobileapi"
            android:required="false"/>

        <activity
            android:name="com.unionpay.uppay.PayActivity"
            android:configChanges="orientation|keyboardHidden"
            android:excludeFromRecents="true"
            android:label="@string/app_name"
            android:screenOrientation="portrait"
            android:windowSoftInputMode="adjustResize"/>

        <activity
            android:name="com.unionpay.UPPayWapActivity"
            android:configChanges="orientation|keyboardHidden"
            android:screenOrientation="portrait"
            android:windowSoftInputMode="adjustResize"/>
        <!--end of 銀聯(lián)支付-->

  1. 我們照舊看下銀聯(lián)支付的類;

/**
 * @author Ly
 * @date 2017/10/24
 * 銀行卡支付界面
 */

public class CardPayActivity extends AppCompatActivity {

    private Button mBtCardPay;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_card);
        initView();
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (data == null) {
            return;
        }

        String msg = "";
        /*
         * 支付控件返回字符串:success、fail恩敌、cancel 分別代表支付成功瞬测,支付失敗,支付取消
         */
        String str = data.getExtras().getString("pay_result");
        if (str.equalsIgnoreCase("success")) {

            // 如果想對結(jié)果數(shù)據(jù)驗簽纠炮,可使用下面這段代碼月趟,但建議不驗簽,直接去商戶后臺查詢交易結(jié)果
            // result_data結(jié)構(gòu)見c)result_data參數(shù)說明
            if (data.hasExtra("result_data")) {
                String result = data.getExtras().getString("result_data");
                try {
                    JSONObject resultJson = new JSONObject(result);
                    String sign = resultJson.getString("sign");
                    String dataOrg = resultJson.getString("data");
                    // 此處的verify建議送去商戶后臺做驗簽
                    // 如要放在手機端驗恢口,則代碼必須支持更新證書
                    boolean ret = verify(dataOrg, sign, AppConf.CardConf.SANDBOX);
                    if (ret) {
                        // 驗簽成功孝宗,顯示支付結(jié)果
                        msg = "支付成功!";
                    } else {
                        // 驗簽失敗
                        msg = "支付失敻纭因妇!";
                    }
                } catch (JSONException e) {
                }
            }
            // 結(jié)果result_data為成功時,去商戶后臺查詢一下再展示成功
            msg = "支付成功猿诸!";
        } else if (str.equalsIgnoreCase("fail")) {
            msg = "支付失斏尘!";
        } else if (str.equalsIgnoreCase("cancel")) {
            msg = "用戶取消了支付";
        }

        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("支付結(jié)果通知");
        builder.setMessage(msg);
        builder.setInverseBackgroundForced(true);
        builder.setNegativeButton("確定", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
            }
        });
        builder.create().show();
    }

    private boolean verify(String dataOrg, String sign, String sandbox) {
        return true;
    }

    private void initView() {
        mBtCardPay = (Button) findViewById(R.id.bt_card_pay);
        mBtCardPay.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                doGetCardPayInfo();
            }
        });
    }

    /**
     * 獲取銀聯(lián)支付的信息
     */
    private void doGetCardPayInfo() {
        OkHttpUtils.get()
                .url(AppConf.NetConf.CARD_PAY_INFO)
                .build().execute(new StringCallback() {
            @Override
            public void onError(Call call, Exception e, int id) {
                e.printStackTrace();
            }

            @Override
            public void onResponse(String response, int id) {
                Log.e("xxxx", response);
                UPPayAssistEx.startPay(CardPayActivity.this, null, null, response, AppConf.CardConf.SANDBOX);
            }
        });
    }


    public static void toCardPayActivity(Activity activity) {
        Intent intent = new Intent(activity, CardPayActivity.class);
        activity.startActivity(intent);
    }
}


其實我不說各位看官也知道了两芳,這個類里面的方法也不多摔寨,就一個:

  • doGetCardPayInfo 獲取服務器上的訂單信息

獲取到了訂單信息后,我們調(diào)用支付的方法:

UPPayAssistEx.startPay(CardPayActivity.this, null, null, response, AppConf.CardConf.SANDBOX);

這里我們注意AppConf.CardConf.SANDBOX怖辆,這里使用的是沙盒模式是复,對前文的配置類還有印象的話,應該記得我使用的是沙盒的模式竖螃,包括獲取tn訂單信息淑廊,也都是沙盒的;當然特咆,這個以后可以替換成app自己的服務器接口季惩。

paypal支付

相信各位看官對這個支付應該是陌生的吧,
可以查看下:https://www.paypal-biz.com/腻格,這個是用在之前的某個海外項目上的画拾,貝寶有2種接入方式,我們這邊使用的是比較老的支付方法

  1. 導入
貝寶導入
  1. 配置

  <!--for paypal-->
        <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" />

        <!--end of paypal-->

貝寶的支付確實是比較麻煩菜职,不止需要導入6個activity青抛,還需要我們導入一個service。

  1. 看下我們的支付類:

/**
 * @author Ly
 * @date 2017/10/24
 */

public class PayPalPayActivity extends AppCompatActivity {

    private Button mBtPaypalPay;


    private String token;

    /**
     * 你在PalPay創(chuàng)建的測試應用客戶端ID
     */
    private static final String CONFIG_CLIENT_ID = AppConf.PayPalConf.CONFIG_CLIENT_ID_LIVE;
    /**
     * 沙盒測試(ENVIRONMENT_SANDBOX)酬核,生產(chǎn)環(huán)境(ENVIRONMENT_PRODUCTION)
     */
    private static PayPalConfiguration config = new PayPalConfiguration()
            .environment(PayPalConfiguration.ENVIRONMENT_PRODUCTION)
            .clientId(CONFIG_CLIENT_ID);
    /**
     * 用于后臺檢驗的paymentId
     */
    private String paymentId = null;

    @Override

    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_paypal);
        startPayPalSer();
        Bundle bundle = getIntent().getBundleExtra("extra");
        if (bundle != null) {
            token = bundle.getString("token");
        }
        initView();
    }

    private void initView() {
        mBtPaypalPay = (Button) findViewById(R.id.bt_paypal_pay);
        mBtPaypalPay.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                doGetPaypalPayInfo();
            }
        });
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        //停止PayPalService服務
        stopService(new Intent(this, PayPalService.class));
    }


    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (resultCode == Activity.RESULT_OK) {
            PaymentConfirmation confirm1 = data
                    .getParcelableExtra(PaymentActivity.EXTRA_RESULT_CONFIRMATION);
            String paymentId;
            try {
                paymentId = confirm1.toJSONObject().getJSONObject("response")
                        .getString("id");
                String paymentClient = confirm1.getPayment().toJSONObject()
                        .toString();
                doSynPaypalOrderStatus(paymentId);
                Log.e("onActivityResult-----", "paymentId: " + paymentId + ", payment_json: "
                        + paymentClient);
            } catch (JSONException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        } else if (resultCode == Activity.RESULT_CANCELED) {
            Log.i("paymentExample", "The user canceled.");
        } else if (resultCode == PaymentActivity.RESULT_EXTRAS_INVALID) {
            Log.i("paymentExample", "An invalid Payment or PayPalConfiguration was submitted. Please see the docs.");
        }
    }

    /**
     * 啟動paypal服務
     */
    private void startPayPalSer() {
        //啟動PayPalService服務
        Intent intent = new Intent(this, PayPalService.class);
        intent.putExtra(PayPalService.EXTRA_PAYPAL_CONFIGURATION, config);
        startService(intent);
    }

    /**
     * 獲取貝寶的支付信息
     */
    private void doGetPaypalPayInfo() {
        OkHttpUtils.post()
                .url(AppConf.NetConf.PAYPAL_PAY_INFO)
                .addParams("accessToken", token)
                .addParams("rechangeUSD", "0.01")
                .build()
                .execute(new StringCallback() {
                    @Override
                    public void onError(Call call, Exception e, int id) {
                        e.printStackTrace();
                    }

                    @Override
                    public void onResponse(String response, int id) {
                        PaypalInfoBean paypalInfoBean = new Gson().fromJson(response, PaypalInfoBean.class);
                        Log.e("xxx", paypalInfoBean.toString());
                        paymentId = paypalInfoBean.getData().getOrderId();
                        // PAYMENT_INTENT_SALE 意思是支付立即完成
                        // 修改 PAYMENT_INTENT_SALE 為 PAYMENT_INTENT_AUTHORIZE to only authorize payment and
                        // capture funds later.
                        PayPalPayment payment = new PayPalPayment(new BigDecimal(paypalInfoBean.getData().getRechangeUSD()), "USD", "充值支付",
                                PayPalPayment.PAYMENT_INTENT_SALE);
                        com.paypal.android.sdk.payments.PayPalItem[] payPalItems =
                                {new PayPalItem(paymentId,
                                        1,
                                        new BigDecimal(paypalInfoBean.getData().getRechangeUSD()),
                                        "USD",
                                        paymentId)};
                        // Total amount
                        BigDecimal subtotal = PayPalItem.getItemTotal(payPalItems);
                        // 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);
                        payment.items(payPalItems).paymentDetails(paymentDetails);
                        Intent intent = new Intent(PayPalPayActivity.this, PaymentActivity.class);
                        intent.putExtra(PaymentActivity.EXTRA_PAYMENT, payment);
                        startActivityForResult(intent, 0);
                    }
                });
    }

    /**
     * 校驗貝寶
     *
     * @param paymentId
     */
    private void doSynPaypalOrderStatus(String paymentId) {
        OkHttpUtils.post()
                .url(AppConf.NetConf.PAYPAL_SYN_ORDER)
                .addParams("accessToken", token)
                .addParams("paymentId", paymentId)
                .build()
                .execute(new StringCallback() {
                    @Override
                    public void onError(Call call, Exception e, int id) {
                        e.printStackTrace();
                    }

                    @Override
                    public void onResponse(String response, int id) {
                        Log.e("貝寶校驗---", response);
                    }
                });
    }

    public static void toPayPalPayActivity(Activity activity, String token) {
        Intent intent = new Intent(activity, PayPalPayActivity.class);
        Bundle bundle = new Bundle();
        bundle.putString("token", token);
        intent.putExtra("extra", bundle);
        activity.startActivity(intent);
    }
}


相信各位看官也會覺得蜜另,這個類...繁瑣了多适室!行,我們慢慢解釋举瑰,有一些注意事項:

  • 在oncreate()需要啟動一個service捣辆,而在ondestory()我們需要stop
  • 為了區(qū)分訂單信息,我們拿到后臺返回的信息后此迅,還需要自己憑借一段標識信息傳遞給服務器(這個是我們服務器自己的需求汽畴,不是每個app都需要)
  PaypalInfoBean paypalInfoBean = new Gson().fromJson(response, PaypalInfoBean.class);
                        Log.e("xxx", paypalInfoBean.toString());
                        paymentId = paypalInfoBean.getData().getOrderId();
                        // PAYMENT_INTENT_SALE 意思是支付立即完成
                        // 修改 PAYMENT_INTENT_SALE 為 PAYMENT_INTENT_AUTHORIZE to only authorize payment and
                        // capture funds later.
                        PayPalPayment payment = new PayPalPayment(new BigDecimal(paypalInfoBean.getData().getRechangeUSD()), "USD", "充值支付",
                                PayPalPayment.PAYMENT_INTENT_SALE);
                        com.paypal.android.sdk.payments.PayPalItem[] payPalItems =
                                {new PayPalItem(paymentId,
                                        1,
                                        new BigDecimal(paypalInfoBean.getData().getRechangeUSD()),
                                        "USD",
                                        paymentId)};
                        // Total amount
                        BigDecimal subtotal = PayPalItem.getItemTotal(payPalItems);
                        // 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);
                        payment.items(payPalItems).paymentDetails(paymentDetails);
                        Intent intent = new Intent(PayPalPayActivity.this, PaymentActivity.class);
                        intent.putExtra(PaymentActivity.EXTRA_PAYMENT, payment);
                        startActivityForResult(intent, 0);

  • 拿到支付結(jié)果后,我們需要請求服務器接口進行校驗邮屁,其實相當于驗簽(這個也是我們服務器的需求,不是每個app都需要的)
    參考方法: doSynPaypalOrderStatus

結(jié)尾

其實我們看了4個支付方案菠齿,有沒有覺得有什么共同點呢:
拿取訂單信息 ---> 吊起支付 ----> 支付后接受callback(視具體情況是否需要驗簽)

總結(jié)

額佑吝,好像也沒什么好說的,上著班偷偷摸摸寫的绳匀,算了芋忿,繼續(xù)打代碼去。
AND
各位程序猿/媛 節(jié)日快樂

AND 毒奶一波

眼淚嘩啦啦地流

祝:諸君與我早日成為程序員

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末疾棵,一起剝皮案震驚了整個濱河市戈钢,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌是尔,老刑警劉巖殉了,帶你破解...
    沈念sama閱讀 216,997評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異拟枚,居然都是意外死亡薪铜,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評論 3 392
  • 文/潘曉璐 我一進店門恩溅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來隔箍,“玉大人,你說我怎么就攤上這事脚乡⊙烟玻” “怎么了?”我有些...
    開封第一講書人閱讀 163,359評論 0 353
  • 文/不壞的土叔 我叫張陵奶稠,是天一觀的道長俯艰。 經(jīng)常有香客問我,道長锌订,這世上最難降的妖魔是什么蟆炊? 我笑而不...
    開封第一講書人閱讀 58,309評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮瀑志,結(jié)果婚禮上涩搓,老公的妹妹穿的比我還像新娘污秆。我一直安慰自己,他們只是感情好昧甘,可當我...
    茶點故事閱讀 67,346評論 6 390
  • 文/花漫 我一把揭開白布良拼。 她就那樣靜靜地躺著,像睡著了一般充边。 火紅的嫁衣襯著肌膚如雪庸推。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,258評論 1 300
  • 那天浇冰,我揣著相機與錄音贬媒,去河邊找鬼。 笑死肘习,一個胖子當著我的面吹牛际乘,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播漂佩,決...
    沈念sama閱讀 40,122評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼脖含,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了投蝉?” 一聲冷哼從身側(cè)響起养葵,我...
    開封第一講書人閱讀 38,970評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎瘩缆,沒想到半個月后关拒,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,403評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡庸娱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,596評論 3 334
  • 正文 我和宋清朗相戀三年夏醉,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片涌韩。...
    茶點故事閱讀 39,769評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡畔柔,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出臣樱,到底是詐尸還是另有隱情靶擦,我是刑警寧澤,帶...
    沈念sama閱讀 35,464評論 5 344
  • 正文 年R本政府宣布雇毫,位于F島的核電站玄捕,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏棚放。R本人自食惡果不足惜枚粘,卻給世界環(huán)境...
    茶點故事閱讀 41,075評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望飘蚯。 院中可真熱鬧馍迄,春花似錦福也、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至赘来,卻和暖如春现喳,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背犬辰。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評論 1 269
  • 我被黑心中介騙來泰國打工嗦篱, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人幌缝。 一個月前我還...
    沈念sama閱讀 47,831評論 2 370
  • 正文 我出身青樓灸促,卻偏偏與公主長得像,于是被迫代替她去往敵國和親狮腿。 傳聞我的和親對象是個殘疾皇子腿宰,可洞房花燭夜當晚...
    茶點故事閱讀 44,678評論 2 354

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理呕诉,服務發(fā)現(xiàn)缘厢,斷路器,智...
    卡卡羅2017閱讀 134,654評論 18 139
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,091評論 25 707
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,806評論 6 342
  • 這不是在說死亡甩挫,不是故去贴硫。說的,只是離開伊者。 那些曾經(jīng)在你生命中出現(xiàn)過的英遭,陪你走過好一段路的人。他們曾在你的生活里帶...
    青馬白恩閱讀 194評論 0 0
  • 規(guī)范化的團隊肯定有嚴格確定的組員行為約束亦渗、行為規(guī)范挖诸。所謂無規(guī)矩不成方圓,方便團隊的管理法精,使團隊成員達成共同的目標多律,...
    yardfarmer閱讀 150評論 0 0