Android三方支付对接方案

发布时间 2023-10-08 14:11:05作者: Tears_fg

场景

用户在APP中下单,跳转到支付宝/微信中完成支付,支付完后跳回到APP内,展示支付结果。

支付宝对接

接入前准备

步骤

  1. 添加支付宝sdk依赖。
    api com.alipay.sdk:alipaysdk-android:+@aar
  2. 拿到后端返回的换起支付宝的支付参数,进行支付。
       private void aliPay(String info) {
    //      Toast.makeText(mContext, "正在支付...", Toast.LENGTH_LONG).show();
            final String orderInfo = info; // 订单信息
            Runnable payRunnable = new Runnable() {
    
                @Override
                public void run() {
                    PayTask alipay = new PayTask(mContext);
                    Map<String, String> result = alipay.payV2(orderInfo, true);
    
                    Message msg = new Message();
                    msg.what = SDK_ALIPAY_FLAG;
                    msg.obj = result;
                    aliPayHandler.sendMessage(msg);
                }
            };
    
            Thread payThread = new Thread(payRunnable);
            payThread.start();
        }
    1. orderInfo:App 支付请求参数字符串,主要包含商家的订单信息,key=value 形式,以 & 连接。
    2. isShowPayLoading:为true,将在唤起pay接口时增加loading作为过渡。
  3. 创建handle接收支付返回的结果。
private Handler aliPayHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == SDK_ALIPAY_FLAG) {

                Map<String, String> payResult = ((Map<String, String>) msg.obj);
                /**
                 对于支付结果,请商户依赖服务端的异步通知结果。同步通知结果,仅作为支付结束的通知。
                 */
                String resultStatus = payResult.get("resultStatus");
                // 判断resultStatus 为9000则代表支付成功
                if (TextUtils.equals(resultStatus, "9000")) {
                    // 该笔订单是否真实支付成功,需要依赖服务端的异步通知。
                    mListener.payResult(1);
                } else {
                    mListener.payResult(-1);
                    // 该笔订单真实的支付结果,需要依赖服务端的异步通知。
//                    ToastUtil.INSTANCE.toast("支付失败");
                }
            }
        }
    };
  1. 拿到支付回调后,轮询后端接口,拿到最终的支付结果。

注意事项

Android10新增了包可见权限,访问外部apk,需要在mainfest中添加对应apk的包名。
<queries>
    <!-- 微信支付客户端-->
    <package android:name="com.tencent.mm" />
    <!-- 支付宝支付客户端-->
    <package android:name="com.eg.android.AlipayGphone" />
</queries>

微信对接

接入前准备

https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_5_1.shtml

步骤

  1. 添加微信sdk依赖。
    implementation com.tencent.mm.opensdk:wechat-sdk-android-without-mta:5.5.8
  2. 在app中进行微信的注册。
    private var wxApi: IWXAPI? = null
    
    fun getWxApi(): IWXAPI? {
        if (wxApi == null) {
            // 注册微信
            wxApi = WXAPIFactory.createWXAPI(ContextHolder.get(), ConstantConfig.WX_APPID)
            wxApi?.registerApp(ConstantConfig.WX_APPID)
        }
        return wxApi
    }
  3. 拿到后端返回的换起微信支付的参数,我们可以转换为微信需要的对象。
    req.appId = ConstantConfig.WX_APPID
    req.partnerId = prePayData.partnerid //这些数据都是接口返回的
    req.timeStamp = prePayData.timestamp
    req.nonceStr = prePayData.noncestr
    req.prepayId = prePayData.prepayid
    req.sign =  prePayData.sign
    req.packageValue = "Sign=WXPay"
    1. appid:微信开放平台审核通过的移动应用appid 。
    2. partnerid:商户号mchid对应的值
    3. prepayid:微信返回的支付交易会话ID,该值有效期为2小时
    4. package:固定值Sign=WXPay
    5. noncestr:随机字符串,不长于32位。
    6. timestamp:时间戳,秒。
    7. sign:签名,使用字段appId、timeStamp、nonceStr、prepayid计算得出的签名值
  4. 唤起支付。
/**
 * @param payFrom 支付来源
 * 微信支付start
 */
fun wxPay(prePayData: PrePayBean, error:()->Unit) {
    if (getWxApi() != null) {
        if (getWxApi()?.isWXAppInstalled == true) {
            val req = PayReq()
            req.appId = ConstantConfig.WX_APPID
            req.partnerId = prePayData.partnerid //这些数据都是接口返回的
            req.timeStamp = prePayData.timestamp
            req.nonceStr = prePayData.noncestr
            req.prepayId = prePayData.prepayid
            req.sign =  prePayData.sign
            req.packageValue = "Sign=WXPay"
            getWxApi()?.sendReq(req)
        } else {
            error.invoke()
            ToastUtil.showNormal( "微信客户端未安装")
        }
    }
}
  1. 在包名目录下建一个wxapi的目录,新建WXPayEntryActivity接收返回的支付。
class WXPayEntryActivity : Activity(), IWXAPIEventHandler {
    private var mContext: Context? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mContext = this@WXPayEntryActivity
        PayUtil.getWxApi()?.handleIntent(intent, this)
    }

    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
        setIntent(intent)
        PayUtil.getWxApi()?.handleIntent(intent, this)

    }



    override fun onReq(baseReq: BaseReq) {
        finish()
    }

    override fun onResp(resp: BaseResp) {
        LogUtils.e( "payResp.onResp ")
        if (resp.type == ConstantsAPI.COMMAND_PAY_BY_WX) {

            val payResp = resp as PayResp
            val extData = payResp.extData //区分支付来源
            val bundle = Bundle()
            LogUtils.e( "payResp.extData = $extData     ${resp.errCode}")
            bundle.putString("payFrom", extData)
            if (resp.errCode == BaseResp.ErrCode.ERR_OK) {
                // 微信支付成功,发个通知
                MsgSender.getInstance().send(Channel.RechargeChannel.WX_PAY_SUCCESS,bundle)
            } else {
                MsgSender.getInstance().send(Channel.RechargeChannel.WX_PAY_FAIL,bundle)

            }
        }else{
            MsgSender.getInstance().send(Channel.RechargeChannel.WX_PAY_FAIL,Bundle())
        }
        finish()
    }
}
记得在manifest文件中注册一下。
<activity
    android:name="kball.winpowerdata.wxapi.WXPayEntryActivity"
    android:exported="true"
    android:launchMode="singleTop"
    android:theme="@android:style/Theme.Translucent.NoTitleBar" />
综上,支付流程就结束了,我们可以拿到支付的结果,成功或者失败去做app自定义的展示。

注意事项

Android10新增了包可见权限,访问外部apk,需要在mainfest中添加对应apk的包名。
<queries>
    <!-- 微信支付客户端-->
    <package android:name="com.tencent.mm" />
    <!-- 支付宝支付客户端-->
    <package android:name="com.eg.android.AlipayGphone" />
</queries>

FAQ

1.android SDK提示:微信appid不对?

注册微信api时需填写微信官网上注册的应用对应的appId。

2.微信唤起失败?

检查WXEntryActivity的目录是否为{packageName}.wxapi,否则微信唤起失败。

3.android SDK提示:支付验证签名失败?

后端拿到签名参数后必须再次签名,务必让后端检查签名是否一样。

4.微信未登录,唤起微信后,将微信退到后台,又返回,页面还是加载中,拿不到微信回调?

目前该场景不会走wxEntryActivity里的onResp方法,考虑手动处理,在换起微信应用后开始计时,在5分钟内拿不到微信回调记为失败情况。