上家公司想要拓展自己在新加坡的市场,打算做一个新加坡本地的生活服务应用,其中少不了的就是支付了。国外支付这块一直是个头疼的问题。想用Google Wallet吧,但它是采用NFC接触式交易,想要进行线上服务时没法进行,后来就去整个贝宝PayPal支付。在这里想吐槽一下,PayPal支付做起来真是头疼,文档阅读起来很吃力,官方也没什么好的demo,无奈,在度娘里不断地搜索,找到了别人做的一个demo,我自己拿来集成了一下,终于弄好了。好了是好了,不过后来老板又重新定了需求,因为嫌PayPal支付的手续费太贵,所以果断弃用,不断打听了解,找到了这个Stripe支付。又是第一次接触这个支付,我呢就把Stripe支付的流程用博客记录下来,以备不时之需,有想要在google play上架自己带支付的应用的小伙伴们也可以参考一下这篇文章。

Stripe Android开发文档地址: https://stripe/docs/mobile/android

第一步,在build.gradle中配置sdk:

compile ‘com.stripe:stripe-android:3.0.1’

要在Eclipse上安装Stripe引用,你需要 :
1. 首先下载 stripe-android 库.
2. 确保你的 Android SDK 最低在 Level 17 以上并且有android-support-v4 包.
3. 导入 stripe 文件夹 到 Eclipse.
4. 在你项目设置里, 在“Android”类别下的“Stripe”部分添加项目依赖库。

如果说没有找到jar包的下载地址,可以用as添加主module依赖:
compile ‘com.stripe:stripe-android:3.0.1’ ,然后在在这里拷贝出来:

第二步,收集信用卡信息

这里需要注意的是,Stripe并没有支付界面,收集信用卡信息的界面需要自己设计。

使用前先导入stripe支付应用的类:

import com.stripe.android.*;
这里会用到两大主类:CardStripe

我们使用用户的输入信息来初始化一个Card:

Card card = new Card(
  cardNumber, //卡号
  cardExpMonth, //卡片过期月份
  cardExpYear, //卡片过期年份
  cardCVC //CVC验证码
);
card.validateNumber(); //检测卡号是否有效
card.validateCVC(); //检测CVC验证码是否有效

第三步,构建Stripe生成token:

if (card.validateCard()) {
     Stripe stripe = new Stripe();
     //调用创建token方法
     stripe.createToken(
      card,//传入card对象
      new TokenCallback() {
        //这里的token打印出来是一串json数据,其中的    token需要用getId()来得到
        public void onSuccess(Token token) {
           // 这里生成得到了token,你需要将它发送到自己服务器,然后服务器利用这个token和支付金额去向    Stripe请求扣费
           submitPaymentInfo(token.getId(),"12.20");//提交支付信息
        }
        public void onError(Exception error) {
          // 显示本地错误信息
          Toast.makeText(getContext(),
            error.getLocalizedString(getContext()),
            Toast.LENGTH_LONG
          ).show();
        }
      }
    )
}else {//卡号有误
            MToast.shortToast("The card number that you entered is invalid");
        } else if (!card.validateExpiryDate()) {//过期时间有误
            MToast.shortToast("The expiration date that you entered is invalid");
        } else if (!card.validateCVC()) {//CVC验证码有误
            MToast.shortToast("The CVC code that you entered is invalid");
        } else {//卡片详情有误
            MToast.shortToast("The card details that you entered are invalid");
        }
    }

至此,Stripe支付就接入完成了,具体的扣费则是需要自己服务器与stripe服务器交互,在提交完支付信息再得到支付结果过程中进行加载框的处理就看这里就不再多说了。
下面我把我这个类的完整代码放出来吧:

package com.anmu.wannasg.user.ui;

import android.content.Intent;
import android.graphics.drawable.BitmapDrawable;
import android.support.v4.app.DialogFragment;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;

import com.alibaba.fastjson.JSONObject;
import com.android.volley.AuthFailureError;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import com.anmu.wannasg.R;
import com.anmu.wannasg.SwipeBack.SwipeBackLayout;
import com.anmu.wannasg.application.MyApplication;
import com.anmu.wannasg.autolayout.utils.AutoUtils;
import com.anmu.wannasgmonactivity.BaseSwipeBackActivity;
import com.anmu.wannasg.Keys;
import com.anmu.wannasg.URLS;
import com.anmu.wannasg.utils.L;
import com.anmu.wannasg.utils.MToast;
import com.anmu.wannasg.utils.ScreenUtils;
import com.anmu.wannasg.widget.pickerview.NumberPickerView;
import com.ant.liao.GifView;
import com.stripe.android.Stripe;
import com.stripe.android.TokenCallback;
import com.stripe.android.model.Card;
import com.stripe.android.model.Token;

import java.util.HashMap;
import java.util.Map;

/**
 * Created by Maibenben on 2016/10/27.
 */

public class UEPaymentInputActivity extends BaseSwipeBackActivity {

    private RelativeLayout top;
    private ImageView btnBack;
    private LinearLayout middle;
    private EditText edtCardHolderName;
    private EditText edtCardNumber;
    private EditText edtCvv;
    //    private EditText edtExpiryDate;
    private LinearLayout bottom;
    private NumberPickerView picker_year, picker_month;
    private TextView btnReset;
    private TextView btnNext;
    private TextView txt_price;

    String amount = "";

    String holdername = "", cardnumber = "", cvv = "", expirydate = "";

    Intent mIntent;

    String code;

    int type = 0;

    String[] months = {"01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"};
    String[] years = {"2016", "2017", "2018", "2019", "2020", "2021", "2022", "2023", "2024", "2025", "2026", "2027"
            , "2028", "2029", "2030", "2031", "2032", "2033", "2034", "2035", "2036", "2037", "2038", "2039"};

    @Override
    public void setContentView() {
        mIntent = getIntent();
        if (mIntent != null) {
            amount = mIntent.getStringExtra("amount");
            code = mIntent.getStringExtra("code");
            type = mIntent.getIntExtra("type", 0);
        }
        setContentView(R.layout.u_activity_payment_input);
    }

    @Override
    public void initData() {
        picker_year.refreshByNewDisplayedValues(years);
        picker_month.refreshByNewDisplayedValues(months);
        initPopuptWindow();
        txt_price.setText("S$ " + amount);
    }

    @Override
    public void initWidget() {

        SwipeBackLayout swipeBackLayout = getSwipeBackLayout();
        swipeBackLayout.setEdgeSize(ScreenUtils.getScreenWidth(this) / 10);
        swipeBackLayout.setEdgeTrackingEnabled(MyApplication.getInstance().getCloseModel());

        top = (RelativeLayout) findViewById(R.id.top);
        btnBack = (ImageView) findViewById(R.id.btn_back);
        middle = (LinearLayout) findViewById(R.id.middle);
        edtCardHolderName = (EditText) findViewById(R.id.edt_card_holder_name);
        edtCardNumber = (EditText) findViewById(R.id.edt_card_number);
        edtCvv = (EditText) findViewById(R.id.edt_cvv);
//        edtExpiryDate = (EditText) findViewById(R.id.edt_expiry_date);
        bottom = (LinearLayout) findViewById(R.id.bottom);
        picker_year = (NumberPickerView) findViewById(R.id.picker_year);
        picker_month = (NumberPickerView) findViewById(R.id.picker_month);
        btnReset = (TextView) findViewById(R.id.btn_reset);
        btnNext = (TextView) findViewById(R.id.btn_next);
        txt_price = (TextView) findViewById(R.id.txt_price);

        btnBack.setOnClickListener(this);
        btnReset.setOnClickListener(this);
        btnNext.setOnClickListener(this);

    }

    @Override
    public void widgetClick(View v) {
        switch (v.getId()) {
            case R.id.btn_back:
                scrollToFinishActivity();
                break;
            case R.id.btn_reset:
                edtCardHolderName.setText("");
                edtCardNumber.setText("");
                edtCvv.setText("");
                break;
            case R.id.btn_next:

                holdername = edtCardHolderName.getText().toString().trim();

                cardnumber = edtCardNumber.getText().toString().trim();

                cvv = edtCvv.getText().toString().trim();

                expirydate = picker_year.getContentByCurrValue() + picker_month.getContentByCurrValue();

                if (TextUtils.isEmpty(holdername)) {
                    MToast.shortToast("please enter card holder name");
                    return;
                }
                if (TextUtils.isEmpty(cardnumber)) {
                    MToast.shortToast("please enter card number");
                    return;
                }
                if (TextUtils.isEmpty(cvv)) {
                    MToast.shortToast("please enter cvv");
                    return;
                }
                if (TextUtils.isEmpty(expirydate)) {
                    MToast.shortToast("please enter expiry date");
                    return;
                }
                mLoadingPopupWindow.showAtLocation(loadingView, Gravity.CENTER, 0, 0);
                saveCreditCard(holdername, cardnumber, cvv, expirydate);
                break;
        }
    }

    @Override
    public void getData() {

    }

    @Override
    public void dealData(String response) {

    }

    String url = "";

    private void getMyData(final Token token) {
        url = URLS.USER_STRIPE_PAYMENT;
        StringRequest submitToken = new StringRequest(Request.Method.POST, url, new Response.Listener<String>() {
            @Override
            public void onResponse(String response) {
                dealSubmitResponse(response);
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError volleyError) {
                if (mLoadingPopupWindow.isShowing()) {
                    mLoadingPopupWindow.dismiss();
                }
            }
        }) {
            @Override
            protected Map<String, String> getParams() throws AuthFailureError {
                Map<String, String> params = new HashMap<>();
                params.put("stripeToken", token.getId());
                params.put("code", code);
                return params;
            }
        };
        MyApplication.getQueue().add(submitToken);
    }

    private void dealSubmitResponse(String response) {
        if (TextUtils.isEmpty(response)) {
            return;
        }
        JSONObject jsonObject = JSONObject.parseObject(response);
        int ret = jsonObject.getIntValue("ret");
        if (ret == 200) {
            //success
            MToast.shortToast("Payment Success");
            switch (type) {
                case 2://pickup进来的
                    mIntent = new Intent(this, UPickUpWaitingDetailActivity.class);
                    mIntent.putExtra("code", code);
                    startActivity(mIntent);
                    finish();
                    overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left);
                    break;
                case 4://coupon进来的
                    mIntent = new Intent(this, UCouponSuccessfulPurchaseActivity.class);
                    mIntent.putExtra("code", code);
                    mIntent.putExtra("title", "Successful Purchase");
                    startActivity(mIntent);
                    finish();
                    overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left);
                    break;
            }
        } else {
            MToast.shortToast("sorry,network or server error");
        }
        if (mLoadingPopupWindow.isShowing()) {
            mLoadingPopupWindow.dismiss();
        }
    }

    public void saveCreditCard(String holdername, String cardnumber, String cvv, String expirydate) {
        if (expirydate.length() < 6) {
            MToast.shortToast("Please enter correct expiry date");
            return;
        }
        Card card = new Card(cardnumber, Integer.parseInt(expirydate.substring(4, expirydate.length())), Integer.parseInt(expirydate.substring(0, 4)), cvv);
//        card.setCurrency(form.getCurrency());
        boolean validation = card.validateCard();
        if (validation) {
            new Stripe().createToken(
                    card,
                    Keys.Stripe_Publishable_Key,
                    new TokenCallback() {
                        public void onSuccess(Token token) {
                            getMyData(token);
                            L.i(token.toString() + "\n---------\n" + token.getId());
                        }

                        public void onError(Exception error) {
                            L.i(error.toString());
                        }
                    });
        } else if (!card.validateNumber()) {
            if (mLoadingPopupWindow.isShowing()) {
                mLoadingPopupWindow.dismiss();
            }
            MToast.shortToast("The card number that you entered is invalid");
        } else if (!card.validateExpiryDate()) {
            if (mLoadingPopupWindow.isShowing()) {
                mLoadingPopupWindow.dismiss();
            }
            MToast.shortToast("The expiration date that you entered is invalid");
        } else if (!card.validateCVC()) {
            if (mLoadingPopupWindow.isShowing()) {
                mLoadingPopupWindow.dismiss();
            }
            MToast.shortToast("The CVC code that you entered is invalid");
        } else {
            if (mLoadingPopupWindow.isShowing()) {
                mLoadingPopupWindow.dismiss();
            }
            MToast.shortToast("The card details that you entered are invalid");
        }
    }

    GifView loadingGif;
    /**
     * popupwindow布局
     */
    View loadingView;
    PopupWindow mLoadingPopupWindow;

    /**
     * 创建PopupWindow
     */
    private void initPopuptWindow() {
        LayoutInflater layoutInflater = LayoutInflater.from(this);
        loadingView = layoutInflater.inflate(R.layout.u_popup_payemnt_loading, null);
        AutoUtils.auto(loadingView);
        //View loadingView ;
        //PopupWindow  mLoadingPopupWindow;
        // 创建一个PopupWindow
        // 参数1:contentView 指定PopupWindow的内容
        // 参数2:width 指定PopupWindow的width
        // 参数3:height 指定PopupWindow的height
        mLoadingPopupWindow = new PopupWindow(loadingView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);

        // 设置popwindow如果点击外面区域无法关闭。
        mLoadingPopupWindow.setOutsideTouchable(true);
        // 点击空白处时,隐藏掉pop窗口
        mLoadingPopupWindow.setFocusable(false);
        mLoadingPopupWindow.setBackgroundDrawable(new BitmapDrawable());
        //        backgroundAlpha(0.5f);// 设置背景为半透明
        //        mFilterPopupWindow.showAsDropDown(view_divide, 0, 0);
        loadingGif = (GifView) loadingView.findViewById(R.id.loading_gif);
        loadingGif.setGifImage(R.drawable.loadingview);
    }

}

xml代码:


<?xml version="1.0" encoding="utf-8"?>
<com.anmu.wannasg.autolayout.AutoRelativeLayout
    xmlns:android="http://schemas.android/apk/res/android"
    xmlns:app="http://schemas.android/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white"
    android:fitsSystemWindows="true"
    android:orientation="vertical">

    <RelativeLayout
        android:id="@+id/top"
        android:layout_width="match_parent"
        android:layout_height="81px">

        <ImageView
            android:id="@+id/btn_back"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:scaleType="fitXY"
            android:src="@mipmap/u_img_back_pink"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:gravity="center"
            android:text="ePayment"
            android:textColor="@color/black"
            android:textSize="35px"/>

        <View
            android:layout_width="match_parent"
            android:layout_height="1px"
            android:layout_alignParentBottom="true"
            android:background="@color/lightgrayline"/>
    </RelativeLayout>

    <LinearLayout
        android:id="@+id/middle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/top"
        android:orientation="vertical"
        android:paddingBottom="30px"
        android:paddingLeft="30px"
        android:paddingRight="30px">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="85px"
            android:gravity="center_vertical"
            android:text="Card Holder Name:"
            android:textColor="@color/sixsix"
            android:textSize="30px"/>

        <EditText
            android:id="@+id/edt_card_holder_name"
            android:layout_width="match_parent"
            android:layout_height="70px"
            android:background="@drawable/u_edt_input_selector"
            android:maxEms="50"
            android:maxLines="1"
            android:paddingLeft="20px"
            android:paddingRight="20px"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="85px"
            android:gravity="center_vertical"
            android:text="Card Number:"
            android:textColor="@color/sixsix"
            android:textSize="30px"/>

        <EditText
            android:id="@+id/edt_card_number"
            android:layout_width="match_parent"
            android:layout_height="70px"
            android:background="@drawable/u_edt_input_selector"
            android:inputType="number"
            android:maxEms="50"
            android:maxLines="1"
            android:paddingLeft="20px"
            android:paddingRight="20px"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="85px"
            android:gravity="center_vertical"
            android:text="CVV:"
            android:textColor="@color/sixsix"
            android:textSize="30px"/>

        <EditText
            android:id="@+id/edt_cvv"
            android:layout_width="match_parent"
            android:layout_height="70px"
            android:background="@drawable/u_edt_input_selector"
            android:inputType="number"
            android:maxEms="50"
            android:maxLines="1"
            android:paddingLeft="20px"
            android:paddingRight="20px"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="85px"
            android:gravity="center_vertical"
            android:text="Expiry Date:"
            android:textColor="@color/sixsix"
            android:textSize="30px"/>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="100px"
            android:background="@drawable/u_edt_input_selector">

            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:gravity="center_vertical">

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="85px"
                    android:layout_marginLeft="20px"
                    android:layout_marginRight="40px"
                    android:gravity="center_vertical"
                    android:text="Month:"
                    android:textColor="@color/sixsix"
                    android:textSize="30px"/>

                <com.anmu.wannasg.widget.pickerview.NumberPickerView
                    android:id="@+id/picker_month"
                    android:layout_width="100px"
                    android:layout_height="wrap_content"
                    app:npv_DividerColor="@color/no_color"
                    app:npv_ShowCount="1"
                    app:npv_TextColorHint="@color/sixsix"
                    app:npv_TextColorSelected="#1E64D2"
                    app:npv_TextSizeHint="14sp"
                    app:npv_TextSizeSelected="18sp"/>

            </LinearLayout>

            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:gravity="center_vertical">

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="85px"
                    android:layout_marginLeft="20px"
                    android:layout_marginRight="40px"
                    android:gravity="center_vertical"
                    android:text="Year:"
                    android:textColor="@color/sixsix"
                    android:textSize="30px"/>

                <com.anmu.wannasg.widget.pickerview.NumberPickerView
                    android:id="@+id/picker_year"
                    android:layout_width="100px"
                    android:layout_height="wrap_content"
                    app:npv_DividerColor="@color/no_color"
                    app:npv_ShowCount="1"
                    app:npv_TextColorHint="@color/sixsix"
                    app:npv_TextColorSelected="#1E64D2"
                    app:npv_TextSizeHint="14sp"
                    app:npv_TextSizeSelected="18sp"/>

            </LinearLayout>

        </LinearLayout>

        <!--<EditText-->
        <!--android:id="@+id/edt_expiry_date"-->
        <!--android:layout_width="match_parent"-->
        <!--android:layout_height="70px"-->
        <!--android:background="@drawable/u_edt_input_selector"-->
        <!--android:hint="like 201601"-->
        <!--android:inputType="number"-->
        <!--android:maxEms="50"-->
        <!--android:maxLines="1"-->
        <!--android:paddingLeft="20px"-->
        <!--android:paddingRight="20px"-->
        <!--android:textColorHint="@color/CDCDCD"/>-->

    </LinearLayout>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@id/bottom"
        android:layout_below="@+id/middle"
        android:background="@color/backgroudgray">

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_margin="40px"
            android:orientation="horizontal"
            android:visibility="gone">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginRight="20px"
                android:gravity="center"
                android:text="Need pay"
                android:textColor="@color/user_theme_color"
                android:textSize="35px"/>

            <TextView
                android:id="@+id/txt_price"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:text="S$ 00"
                android:textColor="@color/user_theme_color"
                android:textSize="40px"/>
        </LinearLayout>
    </RelativeLayout>
    <!--<View-->
    <!--android:layout_width="match_parent"-->
    <!--android:layout_above="@id/bottom"-->
    <!--android:layout_below="@+id/middle"-->
    <!--android:background="@color/backgroudgray"-->
    <!--android:layout_height="match_parent"/>-->

    <LinearLayout
        android:id="@+id/bottom"
        android:layout_width="match_parent"
        android:layout_height="120px"
        android:layout_alignParentBottom="true"
        android:background="@color/user_theme_color"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/btn_reset"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="Reset"
            android:textColor="@color/white"
            android:textSize="35px"/>

        <View
            android:layout_width="1px"
            android:layout_height="match_parent"
            android:layout_marginBottom="20px"
            android:layout_marginTop="20px"
            android:background="@color/white"/>

        <TextView
            android:id="@+id/btn_next"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="Next"
            android:textColor="@color/white"
            android:textSize="35px"/>

    </LinearLayout>

</com.anmu.wannasg.autolayout.AutoRelativeLayout>

更多推荐

美国Stripe支付Android端集成流程