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

前言

相信做app的童鞋都知道,我们app 肯定离不开移动支付的,而我们经常使用的支付一般就是微信支付,支付宝支付这2个。
而题主最近刚好离职,也比较闲,刚好总结了之前做过的支付DEMO,整合了微信支付,支付宝支付,银联支付,贝宝支付,整合成一个demo,也算是一个经验总结吧。


丑丑的界面

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

正文

项目结构介绍:

最基础的项目目录结构

其中网络请求我们使用了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 {
        /**
         * 沙盒环境
         */
        String CONFIG_CLIENT_ID_SANDBOX = "xxxxlZPMz2PN3C8akROwHZjDnzmRDGdxt965BkkvVfF8cUlzRU8a2AALVYCPMSc9uwqyJY5";
        /**
         * 正式环境
         */
        String CONFIG_CLIENT_ID_LIVE = "xxxxEiCmYlq83H73jHejGw8r-hyaiL0WbueHIUbOLtSEV_Vfh8rkU4aawFknGQZhA";
    }

    /**
     * 银行卡配置环境
     */
    public interface CardConf {
        /**
         * 银联支付 配置信息
         *
         * @return 01 沙盒环境 00 正式环境
         */
        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支付状态
         */
        String PAYPAL_SYN_ORDER = BASE_URL + "/user/pay/synPaypalOrder.do";
        /**
         * 银联支付 获取tn
         */
        String CARD_PAY_INFO = "http://101.231.204.84:8091/sim/getacptn";
    }
}

微信支付:

微信支付现在已经可以支持依赖导入了。

  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,实现IWXAPIEventHandler接口,其中我们需要注意的是:假设项目包名为 com.ly.PayDemo,那么这个类的路径就应该是com.ly.PayDemo.wxapi.WXPayEntryActivity,包名,路径都不可以错,否则无法进入回调页面!并且记得在AndroidManifest.xml进行注册。
  <!--微信支付成功回调的页面-->
        <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. 值得一提的是,我们要如何接受支付的回调,那就是要靠我们之前大费周章写的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;
        }
    }

在这里我们可以处理下这个页面,而如果我们不需要这个页面而又必须靠这个页面去接受回调,有个小技巧,我们可以把这个页面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);
                    /**
                     * 同步返回的结果必须放置到服务端进行验证(验证的规则请看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”则代表支付成功,具体状态码代表含义可参考接口文档
                    if (TextUtils.equals(resultStatus, "9000")) {
                        Toast.makeText(AliPayActivity.this, "支付成功", Toast.LENGTH_SHORT).show();
                    } else {
                        // 判断resultStatus 为非"9000"则代表可能支付失败
                        // "8000"代表支付结果因为支付渠道原因或者系统原因还在等待支付结果确认,最终交易是否成功以服务端异步通知为准(小概率状态)
                        if (TextUtils.equals(resultStatus, "8000")) {
                            Toast.makeText(AliPayActivity.this, "支付结果确认中", Toast.LENGTH_SHORT).show();

                        } else {
                            // 其他值就可以判断为支付失败,包括用户主动取消支付,或者系统返回的错误
                            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());
                    }
                });
    }

    /**
     * 调用SDK支付
     */
    public void aliPay(final String payInfo) {
        Runnable payRunnable = new Runnable() {
            @Override
            public void run() {
                // 构造PayTask 对象
                PayTask alipay = new PayTask(AliPayActivity.this);
                // 调用支付接口,获取支付结果
                String result = alipay.pay(payInfo, true);
                Message msg = new Message();
                msg.what = SDK_PAY_FLAG;
                msg.obj = result;
                mHandler.sendMessage(msg);
            }
        };
        // 必须异步调用
        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 从服务器获取我们的订单信息。
    获取到了信息后,我们通过调用支付宝的支付api吊起支付:
 /**
     * 调用SDK支付
     */
    public void aliPay(final String payInfo) {
        Runnable payRunnable = new Runnable() {
            @Override
            public void run() {
                // 构造PayTask 对象
                PayTask alipay = new PayTask(AliPayActivity.this);
                // 调用支付接口,获取支付结果
                String result = alipay.pay(payInfo, true);
                Message msg = new Message();
                msg.what = SDK_PAY_FLAG;
                msg.obj = result;
                mHandler.sendMessage(msg);
            }
        };
        // 必须异步调用
        Thread payThread = new Thread(payRunnable);
        payThread.start();
    }

而吊起了支付后,支付结果我们可以在handler中看到,当然!涉及到支付的,我们app本地所谓“支付成功”,"支付失败"都是不靠谱的,最靠谱的还是得靠服务器那边的回调。

银联支付

  1. 导入JAR以及SO
    银联的导入相对比较麻烦,需要导入JAR,SO,还有BIN文件
    银联支付的配置

    因为我们的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. 我们仍旧需要在清单文件进行配置:

  <!--银联支付-->
        <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 银联支付-->

  1. 我们照旧看下银联支付的类;

/**
 * @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")) {

            // 如果想对结果数据验签,可使用下面这段代码,但建议不验签,直接去商户后台查询交易结果
            // result_data结构见c)result_data参数说明
            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) {
                        // 验签成功,显示支付结果
                        msg = "支付成功!";
                    } else {
                        // 验签失败
                        msg = "支付失败!";
                    }
                } catch (JSONException e) {
                }
            }
            // 结果result_data为成功时,去商户后台查询一下再展示成功
            msg = "支付成功!";
        } else if (str.equalsIgnoreCase("fail")) {
            msg = "支付失败!";
        } else if (str.equalsIgnoreCase("cancel")) {
            msg = "用户取消了支付";
        }

        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("支付结果通知");
        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();
            }
        });
    }

    /**
     * 获取银联支付的信息
     */
    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 获取服务器上的订单信息

获取到了订单信息后,我们调用支付的方法:

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创建的测试应用客户端ID
     */
    private static final String CONFIG_CLIENT_ID = AppConf.PayPalConf.CONFIG_CLIENT_ID_LIVE;
    /**
     * 沙盒测试(ENVIRONMENT_SANDBOX),生产环境(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
  • 为了区分订单信息,我们拿到后台返回的信息后,还需要自己凭借一段标识信息传递给服务器(这个是我们服务器自己的需求,不是每个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);

  • 拿到支付结果后,我们需要请求服务器接口进行校验,其实相当于验签(这个也是我们服务器的需求,不是每个app都需要的)
    参考方法: doSynPaypalOrderStatus

结尾

其实我们看了4个支付方案,有没有觉得有什么共同点呢:
拿取订单信息 ---> 吊起支付 ----> 支付后接受callback(视具体情况是否需要验签)

总结

额,好像也没什么好说的,上着班偷偷摸摸写的,算了,继续打代码去。
AND
各位程序猿/媛 节日快乐

AND 毒奶一波

眼泪哗啦啦地流

祝:诸君与我早日成为程序员

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

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,637评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,830评论 25 707
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,778评论 6 342
  • 这不是在说死亡,不是故去。说的,只是离开。 那些曾经在你生命中出现过的,陪你走过好一段路的人。他们曾在你的生活里带...
    青马白恩阅读 190评论 0 0
  • 规范化的团队肯定有严格确定的组员行为约束、行为规范。所谓无规矩不成方圆,方便团队的管理,使团队成员达成共同的目标,...
    yardfarmer阅读 148评论 0 0