第三方支付

第三方支付指的是第三方平台与各银行签约,在买方与卖方之间实现中介担保,从而增强了支付交易的安全性。国内常用的支付平台主要是支付宝和微信支付,其中支付宝的市场份额为71.5%,微信支付的市场份额为15.99%,也就是说这两家垄断了八分之七的支付市场(2015年数据)。除此之外,还有几个app开发会用到的支付平台,包括:银联支付,主要用于公共事业缴费,如水电煤、有线电视、移动电信等等的充值;易宝支付,主要用于各种报名考试的缴费,特别是公务员与事业单位招考;快钱,被万达收购,主要用于航空旅行、教育培训、游戏娱乐等网站的支付;京东支付,主要用于京东商城的支付;百度钱包,主要用于百度系的电商平台。

因为第三方支付只是个中介,交易流程要多次确认,所以app若要集成支付sdk,得进行以下处理:
1、除了作为买方的用户自己拥有支付账号,开发者还得申请作为卖方的商户账号。
2、支付过程中,虽然允许app直接与第三方支付平台通信,但是最好app要有自己的后台服务器,由自己的后台与第三方平台进行通信。这样做的好处是,一方面自己后台掌握了用户交易记录,做账有依据,管理也方便;另一方面,关键交易在自己后台处理,也减少了恶意篡改的风险。
3、为保证信息安全,需对关键数据进行加密处理,如支付宝采用RSA+BASE64算法,微信支付采用MD5算法,银联支付采用RSA算法。有关数据加密算法的说明参见《 Android开发笔记(七十二)数据加密算法》。

支付宝支付

交易流程

支付宝支付的交易流程大致如下:
1、按照指定格式封装好交易信息;
2、对交易信息进行RSA加密与URL编码;
3、调用支付接口,传入加密好的信息串;(这步要另开线程处理,不能放在UI线程中)
4、支付宝sdk在界面下方弹出支付窗口,用户输入支付帐号信息,提交支付;
5、收到支付完成的结果,判断支付状态是成功还是失败;

集成步骤

支付宝sdk的集成比较简单,除了必要的权限外,无需再修改AndroidManifest.xml,jar包也只要导入alipaySdk-20160516.jar。

代码方面,支付宝官方给的demo采用了Thread+Handler的异步处理模式,不过该模式要把代码写在Activity中,不便管理和维护,因此我的测试代码将它改造为Android自带的异步任务处理即AsyncTask方式,有关AsyncTask的说明参见《 Android开发笔记(四十九)异步任务处理AsyncTask》。

测试帐号

支付宝官方demo没有给出测试的商户账号,下面是我在网上找到的测试帐号:

 // 商户PIDpublic static final String PARTNER = "2088811977704990";// 商户收款账号public static final String SELLER = "dev@ifensi.com";// 商户私钥,pkcs8格式public static final String RSA_PRIVATE = "MIICXQIBAAKBgQDlQ468L1A7Q+GG80/Z8f3IsSiiFIluSxfTTSuJ/XSPzvYS+bMZ" +"AQLMqq/nGhkp+1Q5pHF9LAQtQS3gL2pqzbKdtvZSsy/tNFFQcGCsgK2ygMl+MW/F"+"g/ufx7c1jy1kZAeDyl1m302dnRrtSgDalkgH7FKRcmDxbXPTnFGHbg9zMQIDAQAB"+"AoGAa28wGQF28H7L1Yh5V+FtkrlqGCHVkQjBfnRAPea205kheRzoD4SIwk4OJhb1"+"ydWLz4M+53BT+Lz9eXveu3PvCdQe9zMIVC5dKUNVYCvvcHZ+Ot8HriiuwGPb3Quu"+"twbnLGM5gxxPDo0yUyWrfaVn/qR35mS6TDfmgowVG8CmBpECQQDzuhodR/Jgxrtn"+"tka+88alyy+BfjUZqNloPuE7JfXrpOxH5lodk7Y4lTki/dlo5BrK+hrismLFr9Du"+"ueAJ7G9dAkEA8M8C6VnpUMAK5+rYcjKnQssDqcMfurKYEil1BD/TUdSbLI6v8p02"+"mv1ApuTVtQQypZJKIFfurGk0g0QlvzLZ5QJAGfY38+iHDAH/UnPbI1oKTfzPyaZs"+"95fB2NXh3hAUGw7NUHdcIAxs+6gBlxWdRAwQQpDTrlQ8KzyoL9XC5Ku3zQJBALO8"+"j5vEtFTFQl6f9zYlgJpmFTHcpg4fx0mnD+RAD2aAneHADquzlFJSvLLVEn2tyG+0"+"pQdHGqotTDi94L65IdECQQDb1h+5kugCu47IxsDkrLRsKVcr8dSDMORyeT1L0HWR"+"ctramBu+2PBz2UKC6+9dQ+ZQH4XTKpBSvkyZH4mYi1de";
//  // 支付宝公钥
//  public static final String RSA_PUBLIC = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCnxj/9qwVfgoUh/y2W89L6BkRAFljhNhgPdyPuBV64bfQNN1PjbCzkIM6qRdKBoLPXmKKMiFYnkd6rAoprih3/PrQEB/VsW8OoM8fxn67UDYuyBTqA23MML9q1+ilIZwBC2AQ2UBVOrFXfFl75p6/B5KsiNG9zpgmLCUYuLkxpLQIDAQAB";

下面是支付宝支付的效果截图

下面是支付宝支付的示例代码

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Random;import com.alipay.sdk.app.PayTask;
import com.example.exmpay.alipay.bean.AlipayConstants;
import com.example.exmpay.alipay.bean.PayResult;
import com.example.exmpay.alipay.util.SignUtils;import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.text.TextUtils;
import android.widget.Toast;public class AlipayTask extends AsyncTask<Void, Void, String> {private static final String TAG = "AlipayTask";private Context context;private ProgressDialog dialog;public AlipayTask(Context context) {this.context = context;}@Overrideprotected void onPreExecute() {if (TextUtils.isEmpty(AlipayConstants.PARTNER) || TextUtils.isEmpty(AlipayConstants.RSA_PRIVATE) || TextUtils.isEmpty(AlipayConstants.SELLER)) {new AlertDialog.Builder(context).setTitle("警告").setMessage("需要配置PARTNER | RSA_PRIVATE| SELLER").setPositiveButton("确定", new DialogInterface.OnClickListener() {public void onClick(DialogInterface dialoginterface, int i) {}}).show();cancel(true);} else {dialog = ProgressDialog.show(context, "提示", "正在启动支付宝...");}}@Overrideprotected String doInBackground(Void... params) {String orderInfo = getOrderInfo("测试的商品", "该测试商品的详细描述", "0.01");//特别注意,这里的签名逻辑需要放在服务端,切勿将私钥泄露在代码中!String sign = sign(orderInfo);try {//仅需对sign 做URL编码sign = URLEncoder.encode(sign, "UTF-8");} catch (UnsupportedEncodingException e) {e.printStackTrace();}//完整的符合支付宝参数规范的订单信息final String payInfo = orderInfo + "&sign=\"" + sign + "\"&" + getSignType();// 构造PayTask 对象PayTask alipay = new PayTask((Activity) context);// 调用支付接口,获取支付结果String result = alipay.pay(payInfo, false);return result;}@Overrideprotected void onPostExecute(String result) {if (dialog != null) {dialog.dismiss();}PayResult payResult = new PayResult(result);// 支付宝返回此次支付结果及加签,建议对支付宝签名信息拿签约时支付宝提供的公钥做验签String resultInfo = payResult.getResult();Toast.makeText(context, "resultInfo="+resultInfo, Toast.LENGTH_SHORT).show();String resultStatus = payResult.getResultStatus();// 判断resultStatus 为“9000”则代表支付成功,具体状态码代表含义可参考接口文档if (TextUtils.equals(resultStatus, "9000")) {Toast.makeText(context, "支付宝缴费成功", Toast.LENGTH_SHORT).show();} else {// 判断resultStatus 为非“9000”则代表可能支付失败// “8000”代表支付结果因为支付渠道原因或者系统原因还在等待支付结果确认,最终交易是否成功以服务端异步通知为准(小概率状态)if (TextUtils.equals(resultStatus, "8000")) {Toast.makeText(context, "支付宝缴费结果确认中", Toast.LENGTH_SHORT).show();} else {// 其他值就可以判断为支付失败,包括用户主动取消支付,或者系统返回的错误Toast.makeText(context, "支付宝缴费失败"+payResult.getResult(), Toast.LENGTH_SHORT).show();}}}private String getOrderInfo(String subject, String body, String price) {// 签约合作者身份IDString orderInfo = "partner=" + "\"" + AlipayConstants.PARTNER + "\"";// 签约卖家支付宝账号orderInfo += "&seller_id=" + "\"" + AlipayConstants.SELLER + "\"";// 商户网站唯一订单号orderInfo += "&out_trade_no=" + "\"" + getOutTradeNo() + "\"";// 商品名称orderInfo += "&subject=" + "\"" + subject + "\"";// 商品详情orderInfo += "&body=" + "\"" + body + "\"";// 商品金额orderInfo += "&total_fee=" + "\"" + price + "\"";// 服务器异步通知页面路径orderInfo += "¬ify_url=" + "\"" + "http://notify.msp.hk/notify.htm" + "\"";// 服务接口名称, 固定值orderInfo += "&service=\"mobile.securitypay.pay\"";// 支付类型, 固定值orderInfo += "&payment_type=\"1\"";// 参数编码, 固定值orderInfo += "&_input_charset=\"utf-8\"";// 设置未付款交易的超时时间,默认30分钟,一旦超时,该笔交易就会自动被关闭。// 取值范围:1m~15d。m-分钟,h-小时,d-天,1c-当天(无论交易何时创建,都在0点关闭)。// 该参数数值不接受小数点,如1.5h,可转换为90m。orderInfo += "&it_b_pay=\"30m\"";// extern_token为经过快登授权获取到的alipay_open_id,带上此参数用户将使用授权的账户进行支付// orderInfo += "&extern_token=" + "\"" + extern_token + "\"";// 支付宝处理完请求后,当前页面跳转到商户指定页面的路径,可空orderInfo += "&return_url=\"m.alipay.com\"";// 调用银行卡支付,需配置此参数,参与签名, 固定值 (需要签约《无线银行卡快捷支付》才能使用)// orderInfo += "&paymethod=\"expressGateway\"";return orderInfo;}private String getOutTradeNo() {SimpleDateFormat format = new SimpleDateFormat("MMddHHmmss", Locale.getDefault());Date date = new Date();String key = format.format(date);Random r = new Random();key = key + r.nextInt();key = key.substring(0, 15);return key;}private String sign(String content) {return SignUtils.sign(content, AlipayConstants.RSA_PRIVATE);}private String getSignType() {return "sign_type=\"RSA\"";}}

微信支付

交易流程

微信支付的流程分为六个步骤:
1、使用开发者申请到的APP_ID和APP_SECRET向微信平台请求获取access_token;
2、封装订单信息(使用开发者申请到的PARTNER_ID和PARTNER_KEY),并对订单信息进行MD5摘要处理;
3、把加密后的订单与access_token发给微信平台,生成预支付订单,返回预付订单id;
4、重新封装订单信息,加上预付订单id,向微信平台发起支付交易;
5、微信sdk跳到微信支付页面,用户输入支付帐号信息,提交支付;
6、支付完成,进行回调操作;

集成步骤

微信支付的集成与微信分享类似,有关微信分享的介绍参见《 Android开发笔记(一百零五)社会化分享SDK》。下面是集成微信支付时的注意点:
1、要导入专门用于支付的jar包libammsdk.jar,注意这里不能直接用微信分享的jar包,得用官方demo里面的jar包;
2、申请的appid要与工程名对应,appid也要与app打包的签名相对应。注意官方demo的签名文件仅做开发测试用,无法用于打包apk,因为官方签名未提供密码和别名;开发者如果要打发布包,得使用自己的签名文件,并到微信开放平台申请该签名的appid。
3、回调代码WXPayEntryActivity.java必须放在“包名.wxapi”这个package下面,另外AndroidManifest.xml也要补充该Activity的配置;
4、确保你的测试设备上安装了微信,并且已有默认登录的微信账号。如果设备上没装微信,在调用微信支付时会报错“ Failed to find provider info for com.tencent.mm.sdk.plugin.provider”。

测试帐号

下面是微信开放平台官方demo给的测试帐号:

    // APP_ID 替换为你的应用从官方网站申请到的合法appIdpublic static final String APP_ID = "wxd930ea5d5a258f4f";public static final String APP_SECRET = "db426a9829e4b49a0dcac7b4162da6b6";// wxd930ea5d5a258f4f 对应的支付密钥public static final String APP_KEY = "L8LrMqqeGRxST5reouB0K66CaYAWpqhAVsq7ggKkxHCOastWksvuX1uvmvQclxaHoYd3ElNBrNO2DHnnzgfVG9Qs473M3DTOZug5er46FhuGofumV8H2FVR9qkjSlC5K";/** 商家向财付通申请的商家id */public static final String PARTNER_ID = "1900000109";public static final String PARTNER_KEY = "8934e7d15453e97507ef794cf7b0519d";

下面是微信支付的效果截图

下面是微信支付的示例代码
1、获取入口token

import com.example.exmpay.wechat.bean.WechatConstants;
import com.example.exmpay.wechat.bean.GetAccessTokenResult;
import com.example.exmpay.wechat.bean.LocalRetCode;
import com.example.exmpay.wechat.util.WechatUtil;import android.app.ProgressDialog;
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.Toast;public class GetAccessTokenTask extends AsyncTask<Void, Void, GetAccessTokenResult> {private static final String TAG = "GetAccessTokenTask";private Context context;private ProgressDialog dialog;public GetAccessTokenTask(Context context) {this.context = context;}@Overrideprotected void onPreExecute() {dialog = ProgressDialog.show(context, "提示", "正在获取access token...");}@Overrideprotected GetAccessTokenResult doInBackground(Void... params) {GetAccessTokenResult result = new GetAccessTokenResult();String url = String.format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s",WechatConstants.APP_ID, WechatConstants.APP_SECRET);Log.d(TAG, "get access token, url = " + url);byte[] buf = WechatUtil.httpGet(url);if (buf == null || buf.length == 0) {result.localRetCode = LocalRetCode.ERR_HTTP;return result;}String content = new String(buf);result.parseFrom(content);return result;}@Overrideprotected void onPostExecute(GetAccessTokenResult result) {if (dialog != null) {dialog.dismiss();}if (result.localRetCode == LocalRetCode.ERR_OK) {Toast.makeText(context, "获取access token成功, accessToken = " + result.accessToken, Toast.LENGTH_LONG).show();GetPrepayIdTask getPrepayId = new GetPrepayIdTask(context, result.accessToken);getPrepayId.execute();} else {Toast.makeText(context, "获取access token失败,原因: " + result.localRetCode.name(), Toast.LENGTH_LONG).show();}}
}

2、获取预支付订单与进行支付

import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Random;import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.message.BasicNameValuePair;import com.alibaba.fastjson.JSONObject;
import com.example.exmpay.wechat.bean.WechatConstants;
import com.example.exmpay.wechat.bean.GetPrepayIdResult;
import com.example.exmpay.wechat.bean.LocalRetCode;
import com.example.exmpay.wechat.util.MD5;
import com.example.exmpay.wechat.util.WechatUtil;
import com.tencent.mm.sdk.modelpay.PayReq;
import com.tencent.mm.sdk.openapi.IWXAPI;
import com.tencent.mm.sdk.openapi.WXAPIFactory;import android.app.ProgressDialog;
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.Toast;public class GetPrepayIdTask extends AsyncTask<Void, Void, GetPrepayIdResult> {private static final String TAG = "GetPrepayIdTask";private Context context;private ProgressDialog dialog;private String accessToken;public GetPrepayIdTask(Context context, String accessToken) {this.context = context;this.accessToken = accessToken;}@Overrideprotected void onPreExecute() {dialog = ProgressDialog.show(context, "提示", "正在获取预支付订单...");}@Overrideprotected GetPrepayIdResult doInBackground(Void... params) {String url = String.format("https://api.weixin.qq.com/pay/genprepay?access_token=%s", accessToken);String entity = genProductArgs();Log.d(TAG, "doInBackground, url = " + url + ", entity = " + entity);GetPrepayIdResult result = new GetPrepayIdResult();byte[] buf = WechatUtil.httpPost(url, entity);if (buf == null || buf.length == 0) {result.localRetCode = LocalRetCode.ERR_HTTP;return result;}String content = new String(buf);Log.d(TAG, "doInBackground, response content = " + content);result.parseFrom(content);return result;}@Overrideprotected void onPostExecute(GetPrepayIdResult result) {if (dialog != null) {dialog.dismiss();}if (result.localRetCode == LocalRetCode.ERR_OK) {Toast.makeText(context, "获取prepayid成功", Toast.LENGTH_LONG).show();payWithWechat(result);} else {Toast.makeText(context, "获取prepayid失败,原因"+result.localRetCode.name(), Toast.LENGTH_LONG).show();}}private IWXAPI mWeixinApi;//    // 如果获取token和预付标识在服务器实现,只留下支付动作在客户端实现,那么下面要异步调用
//  private void payWithWechat() {
//      final String payInfo = "";
//
//      Runnable payRunnable = new Runnable() {
//          @Override
//          public void run() {
//              sendWXPayReq(payInfo);
//          }
//      };
//
//      Thread payThread = new Thread(payRunnable);
//      payThread.start();
//  }private String genPackage(List<NameValuePair> params) {StringBuilder sb = new StringBuilder();for (int i = 0; i < params.size(); i++) {sb.append(params.get(i).getName());sb.append('=');sb.append(params.get(i).getValue());sb.append('&');}sb.append("key=");sb.append(WechatConstants.PARTNER_KEY); // 注意:不能hardcode在客户端,建议genPackage这个过程都由服务器端完成// 进行md5摘要前,params内容为原始内容,未经过url encode处理String packageSign = MD5.getMessageDigest(sb.toString().getBytes()).toUpperCase(Locale.getDefault());return URLEncodedUtils.format(params, "utf-8") + "&sign=" + packageSign;}private String genNonceStr() {Random random = new Random();return MD5.getMessageDigest(String.valueOf(random.nextInt(10000)).getBytes());}private long genTimeStamp() {return System.currentTimeMillis() / 1000;}private String getTraceId() {return "crestxu_" + genTimeStamp(); }private String genOutTradNo() {Random random = new Random();return MD5.getMessageDigest(String.valueOf(random.nextInt(10000)).getBytes());}private long timeStamp;private String nonceStr, packageValue; private String genSign(List<NameValuePair> params) {StringBuilder sb = new StringBuilder();int i = 0;for (; i < params.size() - 1; i++) {sb.append(params.get(i).getName());sb.append('=');sb.append(params.get(i).getValue());sb.append('&');}sb.append(params.get(i).getName());sb.append('=');sb.append(params.get(i).getValue());String sha1 = WechatUtil.sha1(sb.toString());Log.d(TAG, "genSign, sha1 = " + sha1);return sha1;}private String genProductArgs() {JSONObject json = new JSONObject();try {json.put("appid", WechatConstants.APP_ID);String traceId = getTraceId();  // traceId 由开发者自定义,可用于订单的查询与跟踪,建议根据支付用户信息生成此idjson.put("traceid", traceId);nonceStr = genNonceStr();json.put("noncestr", nonceStr);List<NameValuePair> packageParams = new LinkedList<NameValuePair>();packageParams.add(new BasicNameValuePair("bank_type", "WX"));packageParams.add(new BasicNameValuePair("body", "千足金箍棒"));packageParams.add(new BasicNameValuePair("fee_type", "1"));packageParams.add(new BasicNameValuePair("input_charset", "UTF-8"));packageParams.add(new BasicNameValuePair("notify_url", "http://weixin.qq.com"));packageParams.add(new BasicNameValuePair("out_trade_no", genOutTradNo()));packageParams.add(new BasicNameValuePair("partner", WechatConstants.PARTNER_ID));packageParams.add(new BasicNameValuePair("spbill_create_ip", "196.168.1.1"));packageParams.add(new BasicNameValuePair("total_fee", "1"));packageValue = genPackage(packageParams);json.put("package", packageValue);timeStamp = genTimeStamp();json.put("timestamp", timeStamp);List<NameValuePair> signParams = new LinkedList<NameValuePair>();signParams.add(new BasicNameValuePair("appid", WechatConstants.APP_ID));signParams.add(new BasicNameValuePair("appkey", WechatConstants.APP_KEY));signParams.add(new BasicNameValuePair("noncestr", nonceStr));signParams.add(new BasicNameValuePair("package", packageValue));signParams.add(new BasicNameValuePair("timestamp", String.valueOf(timeStamp)));signParams.add(new BasicNameValuePair("traceid", traceId));json.put("app_signature", genSign(signParams));json.put("sign_method", "sha1");} catch (Exception e) {Log.e(TAG, "genProductArgs fail, ex = " + e.getMessage());return null;}return json.toString();}private void payWithWechat(GetPrepayIdResult result) {PayReq req = new PayReq();req.appId = WechatConstants.APP_ID;req.partnerId = WechatConstants.PARTNER_ID;req.prepayId = result.prepayId;req.nonceStr = nonceStr;req.timeStamp = String.valueOf(timeStamp);req.packageValue = "Sign=" + packageValue;List<NameValuePair> signParams = new LinkedList<NameValuePair>();signParams.add(new BasicNameValuePair("appid", req.appId));signParams.add(new BasicNameValuePair("appkey", WechatConstants.APP_KEY));signParams.add(new BasicNameValuePair("noncestr", req.nonceStr));signParams.add(new BasicNameValuePair("package", req.packageValue));signParams.add(new BasicNameValuePair("partnerid", req.partnerId));signParams.add(new BasicNameValuePair("prepayid", req.prepayId));signParams.add(new BasicNameValuePair("timestamp", req.timeStamp));req.sign = genSign(signParams);mWeixinApi = WXAPIFactory.createWXAPI(context, WechatConstants.APP_ID);// 在支付之前,如果应用没有注册到微信,应该先调用IWXMsg.registerApp将应用注册到微信mWeixinApi.sendReq(req);}
}

3、完成支付后的回调

import net.sourceforge.simcpux.R;import com.example.exmpay.wechat.bean.WechatConstants;
import com.tencent.mm.sdk.constants.ConstantsAPI;
import com.tencent.mm.sdk.modelbase.BaseReq;
import com.tencent.mm.sdk.modelbase.BaseResp;
import com.tencent.mm.sdk.modelbase.BaseResp.ErrCode;
import com.tencent.mm.sdk.openapi.IWXAPI;
import com.tencent.mm.sdk.openapi.IWXAPIEventHandler;
import com.tencent.mm.sdk.openapi.WXAPIFactory;import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;public class WXPayEntryActivity extends Activity implements IWXAPIEventHandler {private static final String TAG = "WXPayEntryActivity";private IWXAPI api;private TextView tv_result;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.pay_result);tv_result = (TextView) findViewById(R.id.tv_result);api = WXAPIFactory.createWXAPI(this, WechatConstants.APP_ID);api.handleIntent(getIntent(), this);}@Overrideprotected void onNewIntent(Intent intent) {super.onNewIntent(intent);setIntent(intent);api.handleIntent(intent, this);}@Overridepublic void onReq(BaseReq req) {}@Overridepublic void onResp(BaseResp resp) {Log.d(TAG, "onResp, errCode = " + resp.errCode);String result = "";if (resp.getType() == ConstantsAPI.COMMAND_PAY_BY_WX) {switch (resp.errCode) {case ErrCode.ERR_OK:result = "微信支付成功";break;case ErrCode.ERR_COMM:result = "微信支付失败:"+resp.errCode+","+resp.errStr;break;case ErrCode.ERR_USER_CANCEL:result = "微信支付取消:"+resp.errCode+","+resp.errStr;break;default:result = "微信支付未知异常:"+resp.errCode+","+resp.errStr;break;}}Toast.makeText(this, result, Toast.LENGTH_LONG).show();tv_result.setText(result);}}

银联支付

交易流程

银联支付的流程步骤见下:
1、向银联平台申请一个交易流水号;
2、向商户自己的后台发起付费交易(如果有自己后台的话);
3、使用交易流水号向银联平台发起支付请求;
4、银联sdk跳到银联支付页面,用户输入银行卡号、手机号码及验证码,提交支付;
5、支付完成,进行回调处理,返回串里有签名信息,app要传回商户后台进行验证,并确认交易;

集成步骤

银联支付的集成略微复杂,注意点如下:
1、libs目录下加入jar包UPPayAssistEx.jar和UPPayPluginExPro.jar,以及so库文件;
2、assets目录下加入data.bin文件;
3、AndroidManifest.xml加入两个银联页面的定义,示例如下:

     <!-- 银联支付 --><uses-library android:name="org.simalliance.openmobileapi" android:required="false"/><!-- 银联支付页面 --><activityandroid:name="com.unionpay.uppay.PayActivity"android:configChanges="orientation|keyboardHidden|keyboard"android:screenOrientation="portrait"></activity><activityandroid:name="com.unionpay.UPPayWapActivity"android:configChanges="orientation|keyboardHidden|fontScale"android:screenOrientation="portrait"android:windowSoftInputMode="adjustResize" ></activity>

4、在启动银联支付的Activity代码中,重写方法onActivityResult,对支付结果的返回包进行验证处理;

测试帐号

银联支付的官方demo下载页面是https://open.unionpay.com/ajweb/help/file,下面是官方demo的测试帐号:

银行卡号:6226090000000048
手机号:18100000000
短信验证码:123456(先点获取验证码之后再输入)

下面是银联支付的效果截图
输入银行卡号

输入手机号码与验证码

下面是银联支付的示例代码

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;import org.json.JSONException;
import org.json.JSONObject;import com.example.exmpay.unionpay.bean.UnionpayConstants;
import com.unionpay.UPPayAssistEx;import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.Toast;public class UnionpayTask extends AsyncTask<Void, Void, String> {private static final String TAG = "UnionpayTask";private Context context;private ProgressDialog dialog;public static final int PLUGIN_VALID = 0;public static final int PLUGIN_NOT_INSTALLED = -1;public static final int PLUGIN_NEED_UPGRADE = 2;public UnionpayTask(Context context) {this.context = context;}@Overrideprotected void onPreExecute() {dialog = ProgressDialog.show(context, "提示", "正在启动银联支付...");}@Overrideprotected String doInBackground(Void... params) {Log.d(TAG, "doInBackground");String tn = null;InputStream is;try {String url = UnionpayConstants.TN_URL_01;URL myURL = new URL(url);URLConnection ucon = myURL.openConnection();ucon.setConnectTimeout(120000);is = ucon.getInputStream();int i = -1;ByteArrayOutputStream baos = new ByteArrayOutputStream();while ((i = is.read()) != -1) {baos.write(i);}tn = baos.toString();is.close();baos.close();} catch (Exception e) {e.printStackTrace();}Log.d(TAG, "tn="+tn);return tn;}@Overrideprotected void onPostExecute(String tn) {if (dialog != null) {dialog.dismiss();}startpay(tn);UPPayAssistEx.startPay(context, null, null, tn, UnionpayConstants.MODE);}public static void dealResult(Context context, Intent data) {if (data == null) {return;}String msg = "";//支付控件返回字符串:success、fail、cancel 分别代表支付成功,支付失败,支付取消String str = data.getExtras().getString("pay_result");if (str.equalsIgnoreCase("success")) {// 支付成功后,extra中如果存在result_data,取出校验// result_data结构见c)result_data参数说明if (data.hasExtra("result_data")) {String result = data.getExtras().getString("result_data");Log.d(TAG, "result="+result);try {JSONObject resultJson = new JSONObject(result);String sign = resultJson.getString("sign");String dataOrg = resultJson.getString("data");// 验签证书同后台验签证书// 此处的verify,商户需送去商户后台做验签boolean ret = verify(dataOrg, sign, UnionpayConstants.MODE);if (ret) {// 验证通过后,显示支付结果msg = "银联支付成功";} else {// 验证不通过后的处理// 建议通过商户后台查询支付结果msg = "银联支付失败:验证失败";}} catch (JSONException e) {}} else {// 未收到签名信息// 建议通过商户后台查询支付结果msg = "银联支付成功,但未收到签名信息";}} else if (str.equalsIgnoreCase("fail")) {msg = "银联支付失败";} else if (str.equalsIgnoreCase("cancel")) {msg = "银联支付取消";}Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();}private int startpay(String tn) {//商户后台要保存交易流水号return 0;}private static boolean verify(String msg, String sign64, String mode) {// 此处的verify,商户需送去商户后台做验签return true;}}

点此查看Android开发笔记的完整目录

Android开发笔记(一百零六)支付缴费SDK相关推荐

  1. Android开发笔记(九十六)集合动画与属性动画

    集合动画AnimationSet 补间动画有四大类:透明度动画AlphaAnimation.旋转动画RotateAnimation.缩放动画ScaleAnimation.平移动画TranslateAn ...

  2. Android开发笔记(十六)秋千摇摆动画SwingAnimation

    上节博主介绍了AlphaAnimation和淡入淡出动画的使用,其实AlphaAnimation只是四种补间动画中的一种.那么为了加深对其他补间动画的理解,我想说说旋转动画RotateAnimatio ...

  3. Android开发笔记(八十二)SDK版本兼容

    统一主题与风格 Android控件很多属性都有默认值,比如文字默认黑色.编辑框默认透明背景等等,但因为android是开源的,各厂商都会自行修修补补,所以很多时候默认值并不靠谱.举例如下: 1.在某些 ...

  4. Android开发笔记(一百二十六)自定义音乐播放器

    MediaRecorder/MediaPlayer 在Android手机上面,音频的处理比视频还要复杂,这真是出人意料.在前面的博文< Android开发笔记(五十七)录像录音与播放>中, ...

  5. Android开发笔记(八十六)几个特殊的类

    接口interface interface是一些功能的集合,但它只定义了对象必须实现的成员,而不包含成员的实现代码,成员的具体代码由实现接口的类提供.Android对接口的使用场景主要有三类:事件监听 ...

  6. Android开发笔记(序)写在前面的目录

    知识点分类 一方面写写自己走过的弯路掉进去的坑,避免以后再犯:另一方面希望通过分享自己的经验教训,与网友互相切磋,从而去芜存菁进一步提升自己的水平.因此博主就想,入门的东西咱就不写了,人不能老停留在入 ...

  7. Android开发笔记(序)

    本开发笔记,借鉴与其他开发者整理的文章范例与心得体会.在这里作为开发过程中的一个总结与笔记式记录. 如有侵犯作者权益,请及时联系告知删除.俗话说:集百家成一言,去粕成金. ************** ...

  8. Android开发笔记(序)写在前面的目录大全

    转自  湖前琴亭 的博客https://blog.csdn.net/aqi00/article/details/50012511 知识点分类 一方面写写自己走过的弯路掉进去的坑,避免以后再犯:另一方面 ...

  9. Andriod开发之二十:Android开发笔记(序)写在前面的目录

    https://blog.csdn.net/aqi00/article/details/50038385 知识点分类 一方面写写自己走过的弯路掉进去的坑,避免以后再犯:另一方面希望通过分享自己的经验教 ...

  10. Android开发笔记(九十)建造者模式

    基本概念 建造者模式是一种常用的设计模式,它用于把类的表现和构建分离开来.引入建造者模式的缘由,且看博主下面细细道来. 公开属性 一般我们定义一个类的属性,如果属性是公开的,那可以直接对该类的属性赋值 ...

最新文章

  1. 【摩天好课推荐】数字时代职业生涯规划
  2. Kafka幂等性与事务
  3. python idle运行anaconda_在Python IDLE 下调用anaconda中的库教程
  4. 接口测试用python怎么做_请问一下python怎么做接口测试工具?
  5. Java 集合系列16之 HashSet详细介绍(源码解析)和使用示例
  6. java判断用户是否在某一个区域登录_单点登录实现原理
  7. 实现一个简易的线程池。
  8. 手机计算机不支持此操作系统,Win10计算机投影屏幕此设备不支持Miracast
  9. linux mysql配置文件
  10. Informatic中如何设置每月初,和每周五、周六跑批
  11. linux 版的 wps 缺少字体的解决办法
  12. E企云企业邮箱,选了不后悔
  13. Xcode6内置环境变量(Build Setting Macros)
  14. 淘晶驰串口屏入门(四)进度条、滑块、定时器、单选框、复选框、二维码
  15. 【龙芯1c库】封装模拟I2C接口和使用示例
  16. MessageFormatter
  17. Unity游戏画面参数解析与应用:垂直同步、动态模糊、抗锯齿
  18. 新用户启动Anaconda时,一直卡在loading applications
  19. 拿下今年最大笔潮玩市场融资,52TOYS投资价值几何?
  20. 运放单的电源供电和双电源供电

热门文章

  1. TensorFlow2.0:张量排序
  2. 架构之技术复杂度与业务复杂度
  3. git2.29.2..2安装_Centos6.5+jumpserver组件安装(2)
  4. 知道焊缝长度如何确定节点板尺寸_悦和分享:最全的关于焊缝无损检测的知识点...
  5. echarts vue 柱状图实例_「源码学习」适用于 Vue3 的 ECharts 包装组件
  6. 12.12 带触发器按钮的输入框
  7. Git检出指定的目录-稀疏检出
  8. Java 之 Annotation(注解)介绍
  9. java .do是什么_为什么Java Web应用程序使用.do扩展名?它从哪里来的?
  10. 动态html树形菜单模板,JS+CSS简易树状菜单Tree