Android 一行代码解决支付宝、微信、银联支付

xiaoxiao2021-02-28  168

前言

在Android 中只要提到支付,就必然会使用支付宝、微信、银联这三大支付渠道,所以只要遇到支付需求,就必须去跟着三大支付渠道的文档去对接,每次都是体力活,能不能把三大支付渠写成统一的支付模块,只需要对支付所需要的信息进行配置,一句方法调用即可完成支付?

这篇博客就是完成这个需求的,下面让我来详细说明实现步骤。

准备

请务必到支付宝、银联、微信,下载好Android所需要的SDK备用。支付宝Sdk 使用的是蚂蚁金服下的2.0版本。银联支付,请使用2016年12月后发布的版本,因为该版本已经将插件支付和内置支付合并在一起了。不需要检测是否安装插件。

支付所需数据

支付宝

字段说明orderInfo支付宝2.0SDK中在服务器端生成好的订单信息

参考网址:

https://doc.open.alipay.com/docs/doc.htm?spm=a219a.7629140.0.0.R90GnU&treeId=59&articleId=103663&docType=1

使用建造者模式,创建支付宝支付所需数据

public class AliPayConfig { private String orderInfo; private Activity context; private AliPayConfig() { } public String getOrderInfo() { return orderInfo; } public Activity getContext() { return context; } public static class Builder { private String orderInfo; private Activity context; public Builder() { super(); } public AliPayConfig.Builder with(Activity context) { this.context = context; return this; } /** * 设置支付宝支付OrderInfo * * @param orderInfo * @return */ public AliPayConfig.Builder setOrderInfo(String orderInfo) { this.orderInfo = orderInfo; return this; } public AliPayConfig build() { AliPayConfig aliPayPayConfig = new AliPayConfig(); aliPayPayConfig.context = this.context; aliPayPayConfig.orderInfo = this.orderInfo; return aliPayPayConfig; } } }

微信

字段说明appId微信支付AppIDpartnerId支付商户号prepayId预支付码(重要)packageValuepackageValuenonceStrnonceStrtimeStamp时间戳sign签名

参考网址:

https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_12&index=2

使用建造者模式创建微信支付所需数据

public class WeiXinPayConfig { private Activity context; private String appId; private String partnerId; private String prepayId; private String packageValue; private String nonceStr; private String timeStamp; private String sign; private WeiXinPayConfig() { } public Activity getContext() { return context; } public String getAppId() { return appId; } public String getPartnerId() { return partnerId; } public String getPrepayId() { return prepayId; } public String getPackageValue() { return packageValue; } public String getNonceStr() { return nonceStr; } public String getTimeStamp() { return timeStamp; } public String getSign() { return sign; } public static class Builder { private Activity context; //微信支付AppID private String appId; //微信支付商户号 private String partnerId; //预支付码(重要) private String prepayId; //"Sign=WXPay" private String packageValue = "Sign=WXPay"; private String nonceStr; //时间戳 private String timeStamp; //签名 private String sign; public Builder() { super(); } public Builder with(Activity context) { this.context = context; return this; } /** * 设置微信支付AppID * * @param appId * @return */ public Builder setAppId(String appId) { this.appId = appId; return this; } /** * 微信支付商户号 * * @param partnerId * @return */ public Builder setPartnerId(String partnerId) { this.partnerId = partnerId; return this; } /** * 设置预支付码(重要) * * @param prepayId * @return */ public Builder setPrepayId(String prepayId) { this.prepayId = prepayId; return this; } /** * 设置 * * @param packageValue * @return */ public Builder setPackageValue(String packageValue) { this.packageValue = packageValue; return this; } /** * 设置 * * @param nonceStr * @return */ public Builder setNonceStr(String nonceStr) { this.nonceStr = nonceStr; return this; } /** * 设置时间戳 * * @param timeStamp * @return */ public Builder setTimeStamp(String timeStamp) { this.timeStamp = timeStamp; return this; } /** * 设置签名 * * @param sign * @return */ public Builder setSign(String sign) { this.sign = sign; return this; } public WeiXinPayConfig build() { WeiXinPayConfig weiXinPayConfig = new WeiXinPayConfig(); weiXinPayConfig.context = this.context; //微信支付AppID weiXinPayConfig.appId = this.appId; //微信支付商户号 weiXinPayConfig.partnerId = this.partnerId; //预支付码(重要) weiXinPayConfig.prepayId = this.prepayId; //"Sign=WXPay" weiXinPayConfig.packageValue = this.packageValue; weiXinPayConfig.nonceStr = this.nonceStr; //时间戳 weiXinPayConfig.timeStamp = this.timeStamp; //签名 weiXinPayConfig.sign = this.sign; return weiXinPayConfig; } } }

银联

字段说明tradeCode文档中描述的TnserverModel文档中描述的serverModel

参考网址:

https://open.unionpay.com/ajweb/help/file/techFile?productId=3

手机控件支付开发包(Android 版/app开发包/控件使用指南/中国银联手机支付控件接入指南Android.doc

使用建造者模式创建银联所需数据

public class UnionBankPayConfig { private Activity context; private String tradeCode; private String serverModel; private UnionBankPayConfig() { } public Activity getContext() { return context; } public String getTradeCode() { return tradeCode; } public String getServerModel() { return serverModel; } public static class Builder { private Activity context; private String tradeCode; private String serverModel; public Builder() { super(); } public UnionBankPayConfig.Builder with(Activity context) { this.context = context; return this; } /** * 设置银联支付Tn * * @param tradeCode * @return */ public UnionBankPayConfig.Builder setTradeCode(String tradeCode) { this.tradeCode = tradeCode; return this; } /** * 设置银联支付服务模式 * * @param serverModel “00” – 银联正式环境 ,“01” – 银联测试环境,该环境中不发生真实交易 * @return */ public UnionBankPayConfig.Builder setServerModel(String serverModel) { this.serverModel = serverModel; return this; } public UnionBankPayConfig build() { UnionBankPayConfig unionBankPayConfig = new UnionBankPayConfig(); unionBankPayConfig.context = this.context; unionBankPayConfig.tradeCode = this.tradeCode; unionBankPayConfig.serverModel = this.serverModel; return unionBankPayConfig; } } }

抽象PayFunction

根据阅读三家支付渠道文档,发现,其实大致步骤可以归结为两步。

检查是否安装有支付渠道插件支付,获取同步返回信息

提取共性,创建PayFunction接口

public interface PayFunction { void payOrder(); void checkPayState(CheckStateListener checkStateListener); }

支付宝

import android.os.Handler; import android.os.Message; import android.text.TextUtils; import android.widget.Toast; import com.alipay.sdk.app.PayTask; import com.ta.utdid2.android.utils.StringUtils; import org.unreal.common.pay.CheckStateListener; import org.unreal.common.pay.PayFunction; import org.unreal.common.pay.PayResultListener; import java.util.Map; public class AliPay implements PayFunction { private static final int SDK_PAY_FLAG = 1; private static final int SDK_CHECK_FLAG = 2; private AliPayConfig config; private PayResultListener listener; private Handler mHandler; private CheckStateListener checkStateListener; public AliPay(PayResultListener listener) { this.listener = listener; } @Override public void payOrder() { Runnable checkRunnable = new Runnable() { @Override public void run() { // 构造PayTask 对象 PayTask payTask = new PayTask(config.getContext()); // 调用查询接口,获取查询结果 Map<String, String> result = payTask.payV2(config.getOrderInfo(), true); Message msg = new Message(); msg.what = SDK_PAY_FLAG; msg.obj = result; mHandler.sendMessage(msg); } }; Thread payThread = new Thread(checkRunnable); payThread.start(); } @Override public void checkPayState(CheckStateListener checkStateListener) { this.checkStateListener = checkStateListener; Runnable checkRunnable = new Runnable() { @Override public void run() { // 构造PayTask 对象 PayTask payTask = new PayTask(config.getContext()); // 调用查询接口,获取查询结果 String result = payTask.getVersion(); Message msg = new Message(); msg.what = SDK_CHECK_FLAG; msg.obj = result; mHandler.sendMessage(msg); } }; Thread checkThread = new Thread(checkRunnable); checkThread.start(); } public AliPay setConfig(final AliPayConfig config) { this.config = config; // 支付宝返回此次支付结果及加签,建议对支付宝签名信息拿签约时支付宝提供的公钥做验签 // String resultInfo = payResult.getResult(); // 判断resultStatus 为“9000”则代表支付成功,具体状态码代表含义可参考接口文档 // 判断resultStatus 为非“9000”则代表可能支付失败 // “8000”代表支付结果因为支付渠道原因或者系统原因还在等待支付结果确认,最终交易是否成功以服务端异步通知为准(小概率状态) // 其他值就可以判断为支付失败,包括用户主动取消支付,或者系统返回的错误 mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case SDK_PAY_FLAG: { PayResult payResult = new PayResult((Map<String, String>) msg.obj); // 支付宝返回此次支付结果及加签,建议对支付宝签名信息拿签约时支付宝提供的公钥做验签 // String resultInfo = payResult.getResult(); String resultStatus = payResult.getResultStatus(); // 判断resultStatus 为“9000”则代表支付成功,具体状态码代表含义可参考接口文档 if (TextUtils.equals(resultStatus, "9000")) { Toast.makeText(config.getContext(), "支付宝支付成功", Toast.LENGTH_SHORT).show(); if (listener != null) listener.onPaySuccess(); } else { // 判断resultStatus 为非“9000”则代表可能支付失败 // “8000”代表支付结果因为支付渠道原因或者系统原因还在等待支付结果确认,最终交易是否成功以服务端异步通知为准(小概率状态) if (TextUtils.equals(resultStatus, "8000")) { Toast.makeText(config.getContext(), "支付宝支付结果确认中", Toast.LENGTH_SHORT).show(); if (listener != null) listener.onPayConfirming(); } else { // 其他值就可以判断为支付失败,包括用户主动取消支付,或者系统返回的错误 Toast.makeText(config.getContext(), "支付宝支付失败", Toast.LENGTH_SHORT).show(); if (listener != null) listener.onPayFailure(); } } break; } case SDK_CHECK_FLAG: { if (listener != null) { if (StringUtils.isEmpty(msg.obj.toString())) { Toast.makeText(config.getContext(), "没有安装支付宝或支付宝版本过低,无法使用支付宝支付!" , Toast.LENGTH_SHORT).show(); checkStateListener.checkState(false); } else { checkStateListener.checkState(true); } } break; } default: break; } } }; return this; } }

这种实现方案有漏洞,没有使用公钥验证返回值是否合法,有风险。

import android.text.TextUtils; import java.util.Map; public class PayResult { private String resultStatus; private String result; private String memo; public PayResult(Map<String, String> rawResult) { if (rawResult == null) { return; } for (String key : rawResult.keySet()) { if (TextUtils.equals(key, "resultStatus")) { resultStatus = rawResult.get(key); } else if (TextUtils.equals(key, "result")) { result = rawResult.get(key); } else if (TextUtils.equals(key, "memo")) { memo = rawResult.get(key); } } } @Override public String toString() { return "resultStatus={" + resultStatus + "};memo={" + memo + "};result={" + result + "}"; } /** * @return the resultStatus */ public String getResultStatus() { return resultStatus; } /** * @return the memo */ public String getMemo() { return memo; } /** * @return the result */ public String getResult() { return result; } }

微信

package com.drawthink.pay.weixin; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.support.v4.content.LocalBroadcastManager; import android.widget.Toast; import com.drawthink.pay.PayModule; import com.drawthink.pay.PayResultListener; import com.tencent.mm.opensdk.modelbase.BaseResp; import com.tencent.mm.opensdk.modelpay.PayReq; import com.tencent.mm.opensdk.openapi.IWXAPI; import com.tencent.mm.opensdk.openapi.WXAPIFactory; public class WeiXinPay implements PayModule { //微信支付核心api private IWXAPI mWXApi; private PayResultListener listener; private WeiXinPayConfig weiXinPayConfig; private BroadcastReceiver receiver; public WeiXinPay(WeiXinPayConfig weiXinPayConfig) { this.weiXinPayConfig = weiXinPayConfig; mWXApi = WXAPIFactory.createWXAPI(weiXinPayConfig.getContext(), null); mWXApi.registerApp(weiXinPayConfig.getAppId()); onEvent(); } @Override public PayModule setOnPayResultListener(PayResultListener listener) { this.listener = listener; return this; } @Override public void payOrder() { PayReq request = new PayReq(); request.appId = weiXinPayConfig.getAppId(); request.partnerId = weiXinPayConfig.getPartnerId(); request.prepayId = weiXinPayConfig.getPrepayId(); request.packageValue = weiXinPayConfig.getPackageValue() != null ? weiXinPayConfig.getPackageValue() : "Sign=WXPay"; request.nonceStr = weiXinPayConfig.getNonceStr(); request.timeStamp = weiXinPayConfig.getTimeStamp(); request.sign = weiXinPayConfig.getSign(); request.signType = weiXinPayConfig.getSignType(); mWXApi.sendReq(request); } @Override public void checkPayState() { if(listener != null){ if(!mWXApi.isWXAppInstalled()){ Toast.makeText(weiXinPayConfig.getContext(), "没有安装微信,无法使用微信支付!" , Toast.LENGTH_SHORT).show(); } if(!mWXApi.isWXAppSupportAPI()){ Toast.makeText(weiXinPayConfig.getContext(), "当前微信版本过低,无法使用微信支付,请升级微信!" , Toast.LENGTH_SHORT).show(); } listener.onPayCheck(mWXApi.isWXAppInstalled() && mWXApi.isWXAppSupportAPI()); } } //重要!!!因微信的支付结果回调在yourpackagename.wxapi.WXPayEntryActivity.java中,为了统一处理,所以就在WXPayEntryActivity 中通过本地广播发了一个通知,来处理回调 private void onEvent() { // 0 成功 展示成功页面 // -1 错误 可能的原因:签名错误、未注册APPID、项目设置APPID不正确、注册的APPID与设置的不匹配、其他异常等。 // -2 用户取消 无需处理。发生场景:用户不支付了,点击取消,返回APP。 IntentFilter filter = new IntentFilter("org.unreal.pay.weiXinPayResult"); // 0 成功 展示成功页面 // -1 错误 -2 用户取消 receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { int errCode = intent.getIntExtra("errCode", -1); if (errCode == BaseResp.ErrCode.ERR_OK) { // 0 成功 展示成功页面 WeiXinPay.this.listener.onPaySuccess(); } else {// -1 错误 -2 用户取消 WeiXinPay.this.listener.onPayFailure(); } LocalBroadcastManager.getInstance(weiXinPayConfig.getContext()).unregisterReceiver(receiver); } }; LocalBroadcastManager.getInstance(weiXinPayConfig.getContext()).registerReceiver(receiver,filter); } }

yourpackagename.wxapi.WXPayEntryActivity.java

package com.drawthink.hospital.wxapi; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.provider.SyncStateContract; import android.support.v4.content.LocalBroadcastManager; import com.tencent.mm.opensdk.constants.ConstantsAPI; import com.tencent.mm.opensdk.modelbase.BaseReq; import com.tencent.mm.opensdk.modelbase.BaseResp; import com.tencent.mm.opensdk.openapi.IWXAPI; import com.tencent.mm.opensdk.openapi.IWXAPIEventHandler; import com.tencent.mm.opensdk.openapi.WXAPIFactory; public class WXPayEntryActivity extends Activity implements IWXAPIEventHandler { private IWXAPI api; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); api = WXAPIFactory.createWXAPI(this, null); api.handleIntent(getIntent(), this); } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); setIntent(intent); api.handleIntent(intent, this); } @Override public void onReq(BaseReq req) { } @Override public void onResp(BaseResp resp) { /** * 微信支付成功回调会开启一个activity,并执行onResp方法,我不希望出现这个界面,所以finish了,在这之前,我发送一个广播 * 在广播中我做了回调后的操作 * */ if (resp.getType() == ConstantsAPI.COMMAND_PAY_BY_WX) { //发送广播,为intent添加的String必须一致,接收广播处 LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent("org.unreal.pay.weiXinPayResult").putExtra("errCode", resp.errCode)); } finish(); } }

银联

import android.content.Intent; import android.widget.Toast; import com.unionpay.UPPayAssistEx; import org.unreal.common.pay.CheckStateListener; import org.unreal.common.pay.PayFunction; import org.unreal.common.pay.PayResultListener; public class UnionBankPay implements PayFunction { private PayResultListener listener; private UnionBankPayConfig config; public UnionBankPay(PayResultListener listener) { this.listener = listener; } @Override public void payOrder() { UPPayAssistEx.startPay(config.getContext() , null , null , config.getTradeCode() , config.getServerModel()); } @Override public void checkPayState(CheckStateListener checkStateListener) { checkStateListener.checkState(true); } public void processPayResponse(Intent data) { if (data == null) { return; } String msg = ""; /* * 支付控件返回字符串:success、fail、cancel 分别代表支付成功,支付失败,支付取消 */ String str = data.getExtras().getString("pay_result"); if ("success".equalsIgnoreCase(str)) { // 17-3-3 此处任务只要返回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, mMode); // if (ret) { // // 验签成功,显示支付结果 // msg = "支付成功!"; // } else { // // 验签失败 // msg = "支付失败!"; // } } catch (JSONException e) { } // } // 结果result_data为成功时,去商户后台查询一下再展示成功 Toast.makeText(config.getContext(), "银联支付成功", Toast.LENGTH_SHORT).show(); if (listener != null) { listener.onPaySuccess(); } } else if ("fail".equalsIgnoreCase(str)) { Toast.makeText(config.getContext(), "银联支付失败", Toast.LENGTH_SHORT).show(); if (listener != null) { listener.onPayFailure(); } } else if ("cancel".equalsIgnoreCase(str)) { if (listener != null) { Toast.makeText(config.getContext(), "银联支付失败,用户取消了支付", Toast.LENGTH_SHORT).show(); listener.onPaySuccess(); } } } public UnionBankPay setConfig(UnionBankPayConfig config) { this.config = config; return this; } }

此代码也没有进行远程验证,仅通过支付控件返回字符串判断支付结果,有风险!

检查是否安装支付渠道插件回调接口

主要用于检查插件是否安装,抹除掉同步、异步检查的差别,统一采用回调接口方式来返回检查结果

public interface CheckStateListener { //true 表示 控件安装 false 表示 控件版本过低 或 控件未安装 void checkState(boolean state); }

支付结果回调接口

主要用于支付结果的获取,支付宝提供了三个支付结果,银联、微信提供了两个支付结果。接口设计取了支付宝接口的三个状态,包含了微信、银联支付结果。

public interface PayResultListener { //支付成功 void onPaySuccess(); //支付失败(包含取消支付) void onPayFailure(); //支付结果等待确认中(仅支付宝提供该状态) void onPayConfirming(); }

PayFacade 类,支付调用入口

import android.content.Intent; import org.unreal.common.pay.impl.alipay.AliPay; import org.unreal.common.pay.impl.alipay.AliPayConfig; import org.unreal.common.pay.impl.unionbank.UnionBankPay; import org.unreal.common.pay.impl.unionbank.UnionBankPayConfig; import org.unreal.common.pay.impl.weixin.WeiXinPay; import org.unreal.common.pay.impl.weixin.WeiXinPayConfig; public class PayFacade { private static UnionBankPay unionBankPay; private static WeiXinPay weiXinPay; private static AliPay aliPay; boolean unionBank = false; public PayFacade(PayResultListener listener){ if(aliPay == null) { aliPay = new AliPay(listener); } if(weiXinPay == null) { weiXinPay = new WeiXinPay(listener); } if(unionBankPay == null) { unionBankPay = new UnionBankPay(listener); } } public boolean isUnionBank() { return unionBank; } public void unionProcessResult(Intent intent){ unionBankPay.processPayResponse(intent); } public void pay(final AliPayConfig aliPayConfig) { aliPay.setConfig(aliPayConfig); pay(aliPay); unionBank = false; } public void pay(WeiXinPayConfig weiXinPayConfig){ weiXinPay.setConfig(weiXinPayConfig); pay(weiXinPay); unionBank = false; } public void pay(UnionBankPayConfig unionBankPayConfig){ unionBankPay.setConfig(unionBankPayConfig); pay(unionBankPay); unionBank = true; } private void pay(final PayFunction payModule){ payModule.checkPayState(new CheckStateListener(){ @Override public void checkState(boolean state) { if(state){ payModule.payOrder(); } } }); } }

因为银联的回调需要在onActivityForResult方法中执行,为了调用者不许关心处理过程,特意封装了isUnionBank方法,来进一步处理银联回调。

使用

import android.content.Intent; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; import android.view.View; import org.unreal.common.application.R; import org.unreal.common.pay.PayFacade; import org.unreal.common.pay.PayResultListener; import org.unreal.common.pay.impl.alipay.AliPayConfig; import org.unreal.common.pay.impl.unionbank.UnionBankPayConfig; import org.unreal.common.pay.impl.weixin.WeiXinPayConfig; import butterknife.ButterKnife; import butterknife.OnClick; public class MainActivity extends AppCompatActivity { private PayFacade facade; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); initPayFacade(); } private void initPayFacade() { facade = new PayFacade(new PayResultListener(){ @Override public void onPaySuccess() { } @Override public void onPayFailure() { } @Override public void onPayConfirming() { } }); } @OnClick({R.id.button2, R.id.button3, R.id.button4}) public void onClick(View view) { switch (view.getId()) { case R.id.button2: //支付宝支付(测试参数,请配合实际场景,从服务器取到后,装配到pay方法中使用) //facade.pay(new AliPayConfig.Builder().with(this).build()); break; case R.id.button3: //微信支付(测试参数,请配合实际场景,从服务器取到后,装配到pay方法中使用) //facade.pay(new WeiXinPayConfig.Builder().with(this).build()); break; case R.id.button4: //银联支付(测试参数,请配合实际场景,从服务器取到后,装配到pay方法中使用) //facade.pay(new UnionBankPayConfig.Builder().with(this).build()); break; } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { //银联需要在onActivityResult处理支付结果,为了简化调用者处理,此处判断是否使用银联支付,如果使用银联支付,则将data 传递给封装好的处理方法来处理 if(facade.isUnionBank()){ facade.unionProcessResult(data); } super.onActivityResult(requestCode, resultCode, data); } }

代码地址

https://github.com/ChineseLincoln/Dagger2Mvp/tree/v3

v3 分之下,pay library项目

转载请注明原文地址: https://www.6miu.com/read-18417.html

最新回复(0)