美文网首页编程技术
支付宝、微信,第三方支付SDK接入总结

支付宝、微信,第三方支付SDK接入总结

作者: R_雨泽 | 来源:发表于2017-09-12 18:26 被阅读1067次

    移动端开发文档:

    微信“app支付”移动端开发文档:sdk

    支付宝“手机网站支付转Native支付”移动端开发文档: Sdk

    微信支付宝sdk接入流程

    前言:

    技术有限,仅以集成支付的经历写下此文,希望阅读此文过后能够以native为中心,带动后端、h5快速集成支付sdk。

    支付宝支付

    1.在支付宝开放平台(https://open.alipay.com)创建应用开通“手机网站支付”“APP支付”等必要的权限,在”接口加签方式”,设置好公钥

    2.在(https://docs.open.alipay.com/54/104509)下载sdk,这里我是把jar  sdk中必要我类都复制到真实的项目中进行测试的,因为以前接入过,所以比较随意。

    基本都拿进来了,这样做只是为了尽快调起支付宝,跑通支付流程。

    3.打开AlipayActivity将下面两个参数修改成真实数据

    启动AlipayActivity如下这里点击第一个button你就可以看到支付宝支付了,到这已经把流程走完了。但是真正的工作在刚开始。

    根据业务可以选择支付宝支付和网页支付

    现在了解了支付流程现在开始拆分业务,现在支付宝下单是在app端实现的,为了安全appid还有key都要放到后端,下单的处理逻辑交给后端做,其实就是把app上你实现的下单逻辑给后端用。

    4.1支付宝支付

    三步

    1.App向后端请求订单

    2.调起支付宝支付

    Runnable payRunnable =newRunnable(){

    @Override

    public voidrun(){

    PayTask alipay =newPayTask(AlipayActivity.this);

    Map result = alipay.payV2(orderInfo,true);

    Log.i("msp", result.toString());

    Message msg =newMessage();

    msg.what=SDK_PAY_FLAG;

    msg.obj= result;

    mHandler.sendMessage(msg);

    }

    };

    Thread payThread =newThread(payRunnable);

    payThread.start();

    1.查询支付结果

    PayResult payResult = newPayResult((Map) msg.obj);

    /**

    对于支付结果,请商户依赖服务端的异步通知结果。同步通知结果,仅作为支付结束的通知。

    */

    String resultInfo =

    payResult.getResult();//同步返回需要验证的信息

    String resultStatus =payResult.getResultStatus();

    //返回码,标识支付状态,含义如下:

    //

    9000——订单支付成功

    //

    8000——正在处理中

    //

    4000——订单支付失败

    //

    5000——重复请求

    //

    6001——用户中途取消

    //

    6002——网络连接出错

    if (TextUtils.equals(resultStatus,"9000")) {

    //该笔订单是否真实支付成功,需要依赖服务端的异步通知。

    Toast.makeText(AlipayActivity.this, "支付成功", Toast.LENGTH_SHORT).show();

    } else {

    //该笔订单真实的支付结果,需要依赖服务端的异步通知。

    Toast.makeText(AlipayActivity.this, "支付失败", Toast.LENGTH_SHORT).show();

    }

    注意:这里返回的结果不能作为真实的结果需要在后端查询是否成功

    这也是支付宝下单(https://docs.open.alipay.com/203/107090/)时传notify_url(支付宝服务器主动通知商户服务器里指定的页面 )的作用。

    这里比较简单,不上代码了。

    4.2网页支付

    这里主要的逻辑就是app内嵌h5,由h5向native发起的支付请求。

    先看官方例子(https://docs.open.alipay.com/203/106493/):

    public booleanshouldOverrideUrlLoading(final WebView view, String url) {

    final PayTask task =new PayTask(H5PayDemoActivity.this);

    //处理订单信息

    final String ex =task.fetchOrderInfoFromH5PayUrl(url);

    if(!TextUtils.isEmpty(ex)) {

    //调用支付接口进行支付

    new Thread(new Runnable() {

    public void run() {

    H5PayResultModel result = task.h5Pay(ex, true);

    //处理返回结果

    if (!TextUtils.isEmpty(result.getReturnUrl())) {

    view.loadUrl(result.getReturnUrl());

    }

    }

    }).start();

    } else {

    view.loadUrl(url);

    }

    return true;

    }

    可以看到就是对url进行拦截看看是否是支付宝支付,如果网页都拦截这效率肯定不行,而且有部分项目中已经有的逻辑,还的按原来的套路来,这边提供一种方法作为参考:

    H5用js接口把拼接好的url传递过来,直接用

    final PayTask task = newPayTask(H5PayDemoActivity.this);

    //处理订单信息

    final String ex =task.fetchOrderInfoFromH5PayUrl(url);

    if(!TextUtils.isEmpty(ex)) {

    //调用支付接口进行支付

    new Thread(new Runnable() {

    public void run() {

    H5PayResultModel result = task.h5Pay(ex, true);

    //处理返回结果

    //TODO把结果code  msg返回h5

    //这里有个return_url参数没用

    }

    }).start();

    在把支付的结果返回给h5,然后h5去查询是否支付成功。

    这里给出主要代码:

    在调试的时候如果后端提供给你是一个支付url,那么简单直接webview加载拦截支付ok了,如果测试时后端给的是如下的Form表单数据就需要组装下(重要数据*号代替),

    "

    name=\"punchout_form\" method=\"post\"

    action=\"https://openapi.alipay.com/gateway.do?sign=dHCdmMt1iG1X99JlHDeDuZVVbyYAnCCvLOlRfm1ZbzzEvHsLkoeo8KUe3mSAYeN5c2r%2BBTOZKgrrhtrKT1ZSU2k%2BPSUOC8ubvrPjefd7imH6ZlQTwFs76p1hJvkwzSFRyd4AoUMI1dxLMAywrbs5Us6O4gSzKetSYE0D1zWH2zplP%2FWTTQZ9pt2WH%2BkijxDwjPverQ5WkX6uQ89zoQQ3fHt8tlUX0hb%2BAQwA%2FB%2B7xtqu8F%2F6sVawbcvlavPp9gKwAy1Bf8qtAY7NXTeNNIQJl9fdjzamZPlnMkgidLEfjKfoCeH2DOsvXzIwsJ%2FFYEMDcH6BTtYAufA9AFUeWX7oiw%3D%3D×tamp=2017-07-28+13%3A45%3A14&sign_type=RSA2¬ify_url=**********8&app_id=**********&method=alipay.trade.wap.pay&version=1.0&alipay_sdk=alipay-sdk-java-dynamicVersionNo&format=json\">\n

    type=\"hidden\" name=\"biz_content\"

    value=\"{"out_trade_no":"*****************************

    ","product_code":"QUICK_WAP_PAY","subject":"一元购","total_amount":"0.01"}\">\n

    type=\"submit\" value=\"立即支付\" style=\"display:none\">\n\ndocument.forms[0].submit();"

    //创建完整的html

    private String createALiForm(){

    StringBuffer sb = new StringBuffer();

    sb.append("").append("");

    sb.append("");

    sb.append("").append("表单测试").append("");

    sb.append("");

    sb.append("");

    //       sb.append(respOder.body);

    sb.append(order);

    sb.append("");

    sb.append("");

    //       String  s   = TextUtils.htmlEncode(sb.toString());

    return  sb.toString() ;

    }

    加载方式webView.loadDataWithBaseURL(null,createALiForm(), "text/html", "utf-8", null);

    到这里支付宝支付介绍完了,剩下的就是根据自己的项目coding代码了

    微信支付

    1.到微信开放平台https://pay.weixin.qq.com创建应用,开通支付,添加应用签名,接下来看文档https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_5

    有一步是设置签名和包名

    1.微信比支付宝要繁琐一些,支付步骤为:微信下单-->调起微信支付-->接收支付结果-->查询支付结果。

    下载SDK开发包https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=11_1

    微信下单:具体参数https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1

    调起微信支付:

    接收支付结果:在包名+ .wxapi下新建WXPayEntryActivity接收类

    android:exported="true"

    android:launchMode="singleTop"

    android:screenOrientation="portrait"

    >

    //微信支付回调

    public class WXPayEntryActivity extendsActivity implements IWXAPIEventHandler {

    private static finalString TAG = "WXPayEntryActivity";

    private IWXAPI api;

    @Override

    public voidonCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_wxpay_entry);

    api = WXAPIFactory.createWXAPI(this, WXPUtil.APPID);

    api.handleIntent(getIntent(), this);

    }

    @Override

    protected voidonNewIntent(Intent intent) {

    super.onNewIntent(intent);

    setIntent(intent);

    api.handleIntent(intent, this);

    }

    @Override

    public voidonReq(BaseReq req) {

    }

    @Override

    public voidonResp(BaseResp resp) {

    Log.d(TAG, "onPayFinish, errCode = " + resp.errCode);

    //        0成功展示成功页面

    //        -1错误可能的原因:签名错误、未注册APPID、项目设置APPID不正确、注册的APPID与设置的不匹配、其他异常等。

    //        -2用户取消无需处理。发生场景:用户不支付了,点击取消,返回APP。

    if (resp.getType() == ConstantsAPI.COMMAND_PAY_BY_WX) {

    AlertDialog.Builder builder = new AlertDialog.Builder(this);

    builder.setTitle("提示");

    builder.setMessage(String.format("微信支付结果:%s",String.valueOf(resp.errCode)));

    builder.setOnCancelListener(new DialogInterface.OnCancelListener() {

    public void onCancel(DialogInterface dialog) {

    finish();

    }

    });

    builder.show();

    }

    }

    }

    这里收到支付结果然后再做查询真实测支付结果;

    这里贴出代码:这个工具类有获取随机字符串,下单,数据转XML,调起支付等工具,其他工具在微信开发包内,网络请求依赖okhttp标配了,

    /**

    *

    *

    *注意 商品描述长度有限制, 单位是分

    */public classWXPUtil {

    //微信参数配置public staticStringAPI_KEY="******";

    public staticStringAPPID="****";

    public staticStringMCH_ID="****";

    private static finalStringTAG="WXPUtil";

    private static finalMediaTypeMEDIA_TYPE_FORM= MediaType.parse("application/x-www-form-urlencoded;

    charset=utf-8");//mdiatype这个需要和服务端保持一致private static finalStringBASE_URL="https://api.mch.weixin.qq.com/pay/unifiedorder";//请求接口根地址private staticOkHttpClientmOkHttpClient;//okHttpClient实例staticStringdescription="块钱-这是一个测试产品";

    staticStringsn= System.currentTimeMillis() +"";

    staticDoubletotalAmount= 0.01;

    /**

    *初始化RequestManager

    */private static voidinitOkHttp() {

    //初始化OkHttpClient

    mOkHttpClient=newOkHttpClient().newBuilder()

    .connectTimeout(10, TimeUnit.SECONDS)//设置超时时间.readTimeout(10,TimeUnit.SECONDS)//设置读取超时时间.writeTimeout(10,TimeUnit.SECONDS)//设置写入超时时间.build();

    }

    /**

    *调用 微信支付前 要先注册

    * # registerApp()

    *调起微信支付

    *

    *结果回调在.

    */public static voidloadWXPay(IWXAPI api, Map mresPreOrder) {

    try{

    String timeStamp = String.valueOf(System.currentTimeMillis() /1000);

    Gson gson =newGson();

    String jsonRes = gson.toJson(mresPreOrder);

    ResPreOrder resPreOrder =newResPreOrder();

    resPreOrder = gson.fromJson(jsonRes, ResPreOrder.class);

    PayReq request =newPayReq();

    request.appId= resPreOrder.appid;

    request.partnerId=MCH_ID;//商户号request.prepayId= resPreOrder.prepay_id;

    request.packageValue="Sign=WXPay";//

    request.nonceStr= resPreOrder.nonce_str;//随机字符串request.timeStamp= timeStamp;//时间戳

    //创建signSortedMap sortedMap =newTreeMap();

    sortedMap.put("appid", resPreOrder.appid);

    sortedMap.put("partnerid",MCH_ID);

    sortedMap.put("prepayid", resPreOrder.prepay_id);

    sortedMap.put("package","Sign=WXPay");

    sortedMap.put("noncestr", resPreOrder.nonce_str);

    sortedMap.put("timestamp", timeStamp);//时间戳String sign =createSign(sortedMap);

    request.sign= sign;

    api.sendReq(request);

    }catch(Exception e) {

    Log.e(TAG,"异常:"+ e.getMessage());

    }

    }

    /**

    *有现成的支付参数直接 调起 微信支付

    */public static voidloadWXPay(IWXAPI api) {

    try{

    PayReq request =newPayReq();

    request.appId="******";

    request.partnerId="*****";//商户号request.prepayId="****93061d9c72c700197785826";

    request.packageValue="Sign=WXPay";//

    request.nonceStr="WqQBEjnJcJDhEFsYwc6RZOVqfTLmaM";//随机字符串request.timeStamp="1500866394";//时间戳request.sign="3d9338bf866e849f789e2472d3bb4b70";

    api.sendReq(request);

    }catch(Exception e) {

    Log.e(TAG,"异常:"+ e.getMessage());

    }

    }

    /**

    *获取微信预订单

    *

    *@paramcallBackWX

    */public static voidgetPreOrder(WXOrder wXOrder, CallBackWXcallBackWX) {

    callBackWX.onstart();

    //参数拼接SortedMapparameterMap =newTreeMap();

    parameterMap.put("appid",APPID);

    parameterMap.put("body",cutString(wXOrder.body.replaceAll("[^0-9a-zA-Z\\u4e00-\\u9fa5

    ]",""), 128));

    parameterMap.put("mch_id",MCH_ID);

    parameterMap.put("nonce_str",getStringRandom(32));

    parameterMap.put("notify_url","http://www.com");

    parameterMap.put("out_trade_no", wXOrder.out_trade_no);//商户订单号parameterMap.put("spbill_create_ip",DeviceInfoManager.getDeviceInfo().getIpAddress());

    parameterMap.put("fee_type","CNY");

    BigDecimal b1 =newBigDecimal(wXOrder.total_fee);

    BigDecimal total = b1.multiply(newBigDecimal(100));

    java.text.DecimalFormat df =newjava.text.DecimalFormat("0");

    parameterMap.put("total_fee", df.format(total));

    parameterMap.put("trade_type","APP");

    //非必要字段

    //       parameterMap.put("device_info", wXOrder.device_info);

    //       parameterMap.put("sign_type", wXOrder.sign_type);

    //       parameterMap.put("detail", wXOrder.detail);

    //       parameterMap.put("attach", wXOrder.attach);

    //       parameterMap.put("device_info", wXOrder.device_info);String sign =createSign(parameterMap);

    parameterMap.put("sign", sign);

    String requestXML =getRequestXml(parameterMap);

    Log.i(TAG,"requestXML =="+ requestXML);

    requestPost(BASE_URL,requestXML, callBackWX);

    }

    /**

    *预下单

    * okHttp post同步请求表单提交

    *

    *@param

    url接口地址

    *@param

    params请求参数

    *@param

    callBackWX回调

    */private static voidrequestPost(String url, String params,finalCallBackWX callBackWX) {

    initOkHttp();

    try{

    //创建一个请求finalRequestBody requestbody = RequestBody.create(MEDIA_TYPE_FORM,params);

    Request request =newRequest.Builder()

    .url(url)

    .post(requestbody)

    .build();

    //创建一个Call

    mOkHttpClient.newCall(request).enqueue(newCallback() {

    @Override

    public voidonFailure(Call call, IOException e) {

    callBackWX.onFailure(e.getMessage());

    }

    @Override

    public voidonResponse(Call call, Response response)throwsIOException{

    if(response.isSuccessful()) {

    Map map =null;

    try{

    String sresponse = response.body().string();

    map =doXMLParse(sresponse);

    Log.i(TAG,"preorederresponse =="+ sresponse);

    String isbackok =checkSign(map);

    if(isbackok.equals("nosign") || isbackok.equals("signok")){

    callBackWX.onsuccessful(map);

    }else{//签名不一样callBackWX.onFailure("微信预下单签名错误");

    }

    }catch(JDOMException e) {

    callBackWX.onFailure(e.getMessage());

    e.printStackTrace();

    }catch(IOException e) {

    callBackWX.onFailure(e.getMessage());

    e.printStackTrace();

    }

    }else{

    callBackWX.onFailure(response.message());

    }

    }

    });

    }catch(Exception e) {

    Log.e(TAG, e.toString());

    callBackWX.onFailure(e.getMessage());

    }

    }

    //请求xml组装private staticString getRequestXml(SortedMapparameters) {

    StringBuffer sb =newStringBuffer();

    sb.append("");

    Set es = parameters.entrySet();

    Iterator it = es.iterator();

    while(it.hasNext()) {

    Map.Entry entry = (Map.Entry) it.next();

    String key = (String) entry.getKey();

    String value = (String) entry.getValue();

    if("attach".equalsIgnoreCase(key) ||"body".equalsIgnoreCase(key)||"sign".equalsIgnoreCase(key)) {

    sb.append("<"+ key +">"+"+ value +"]]>+ key +">");

    }else{

    sb.append("<"+ key +">"+ value +"+ key +">");

    }

    }

    sb.append("");

    returnsb.toString();

    }

    /**

    *用于检测微信下单返回参数是否修改

    *

    *@paramparameters

    *@returnnosign:return_code返回不为SUCCESS,signok,signerror

    */private staticString checkSign(Map parameters) {

    if(parameters.get("return_code") ==null&&!parameters.get("return_code").toString().equals("SUCCESS")){

    return "nosign";

    }

    SortedMap sortedMap =newTreeMap();

    Set es = parameters.entrySet();

    Iterator it = es.iterator();

    while(it.hasNext()) {

    Map.Entry entry = (Map.Entry) it.next();

    String k = (String) entry.getKey();

    Object v = entry.getValue();

    if(null!= v && !"".equals(v)

    && !"sign".equals(k)) {

    sortedMap.put(k, v);

    }

    }

    String backsign ="";

    if(parameters.get("sign") !=null) backsign =parameters.get("sign").toString();

    String sign =createSign(sortedMap);

    returnsign.equals(backsign) ?"signok":"signerror";

    }

    //下单生成签名 获取签名https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=4_3private staticString createSign(SortedMap parameters){

    StringBuffer sb =newStringBuffer();

    Set es = parameters.entrySet();

    Iterator it = es.iterator();

    while(it.hasNext()) {

    Map.Entry entry = (Map.Entry) it.next();

    String k = (String) entry.getKey();

    Object v = entry.getValue();

    if(null!= v && !"".equals(v)

    && !"sign".equals(k) && !"key".equals(k)){

    sb.append(k +"="+ v +"&");

    }

    }

    sb.append("key="+API_KEY);

    String sign ="";

    try{

    sign = MD5.getMessageDigest(sb.toString().getBytes("UTF-8")).toUpperCase();

    }catch(UnsupportedEncodingException e) {

    e.printStackTrace();

    }

    returnsign;

    }

    //生成随机 位数字和字母private staticString getStringRandom(intlength) {

    String val ="";

    Random random =newRandom();

    //参数length,表示生成几位随机数for(inti = 0; i < length; i++) {

    String charOrNum = random.nextInt(2) % 2 == 0 ?"char":"num";

    //输出字母还是数字if("char".equalsIgnoreCase(charOrNum)){

    //输出是大写字母还是小写字母inttemp = random.nextInt(2) % 2 == 0 ? 65 :97;

    val += (char) (random.nextInt(26) + temp);

    }else if("num".equalsIgnoreCase(charOrNum)) {

    val += String.valueOf(random.nextInt(10));

    }

    }

    returnval;

    }

    //xml解析public staticMap doXMLParse(String strxml)throwsJDOMException,IOException {

    strxml = strxml.replaceFirst("encoding=\".*\"","encoding=\"UTF-8\"");

    Map m =newHashMap();

    if(null== strxml ||"".equals(strxml)) {

    returnm;

    }

    InputStream in =newByteArrayInputStream(strxml.getBytes("UTF-8"));

    SAXBuilder builder =newSAXBuilder();

    Document doc = builder.build(in);

    Element root = doc.getRootElement();

    List list = root.getChildren();

    Iterator it = list.iterator();

    while(it.hasNext()) {

    Element e = (Element) it.next();

    String k = e.getName();

    String v ="";

    List children = e.getChildren();

    if(children.isEmpty()) {

    v = e.getTextNormalize();

    }else{

    v =getChildrenText(children);

    }

    m.put(k, v);

    }

    //关闭流in.close();

    returnm;

    }

    public staticStringgetChildrenText(List children) {

    StringBuffer sb =newStringBuffer();

    if(!children.isEmpty()) {

    Iterator it = children.iterator();

    while(it.hasNext()) {

    Element e = (Element) it.next();

    String name = e.getName();

    String value = e.getTextNormalize();

    List list = e.getChildren();

    sb.append("<"+ name +">");

    if(!list.isEmpty()) {

    sb.append(getChildrenText(list));

    }

    sb.append(value);

    sb.append("+ name +">");

    }

    }

    returnsb.toString();

    }

    /**

    *截取length -3长度 字符串后加...

    *

    *@paramcontent

    *@paramlength

    *@returncontent为空返回“”;content长度小于length或者 小于3返回原字符串;

    */private staticString cutString(String content,intlength) {

    if(TextUtils.isEmpty(content)) {

    return "";

    }

    if(content.length() < length || length < 3) {

    returncontent;

    }

    content = content.substring(0, length - 3) +"...";

    returncontent;

    }

    publicCallBackWXcallBcak;

    public interfaceCallBackWX{

    voidonstart();

    voidonsuccessful(Map mpreOrder);

    voidonFailure(String msg);

    }

    }

    到这里已经在app端把支付流程除了最后的后端查询支付结果都走了。

    然后也是业务拆分,微信下单给后端做,最后支付的结果查询给h5(因为我们的业务是由h5发起的)

    问题总结

    在实际集成的过程主要有两个问题:

    1.微信支付需要应用签名,这个签名配置不正确会导致支付失败,最好的做法是根据微信提供的工具把app中签名取出来放到微信支付平台;

    2.支付宝支付提供了两种调起支付宝支付的方式个人建议使用PayTask alipay =newPayTask(AliPayActivity.this);,这里遇到的最大问题是订单数据传递,订单数据由后端-->h5 -->app ,在Android和ios接收到数据的时候有可能遇到解析不了,这样后端h5  Android  ios都要跟着反反复复调试,建议以后订单传递采用这种方式:后端生成可以直接调起支付宝的订单,然后base64加密转换下,把转换好的数据直接通过h5传给app,app拿到订单数据后base64解密下直接调起支付宝

    3.没有安装支付宝会调到支付宝H5,没有安装微信,微信提供了方法检测,这里做好判断,做好用户友好设计

    结束语

    集成的过程都是快速的让支付正常使用,然后再根据业务详细coding,两大支付平台对比下来微信支付接入难些,下载的demo没有发现直接支付的流程,然后请求下单,数据封装XML格式,还有返回结果接收都比较不同,写下这个总结方便多接入的开发者快速接入。

    相关文章

      网友评论

      • 9bad611e3e55:你好 想问下微信和支付宝接入时间大概分别为多少啊 谢谢 比较急 在考虑是自主开发还是找第三方直接sdk接入
        9bad611e3e55:@R_雨泽 嗯嗯感谢感谢
        R_雨泽:@nzzzzzz 如果有开发账号还好,没有要准备一些资料,审核也要时间,这里只说开发者账号配置好的情况下,我的应用场景是h5发起支付,SDK接入两天足够了,后面测试断断续续的一两天可以了,主要是支付宝方案选好,仔细看资料,很快的。

      本文标题:支付宝、微信,第三方支付SDK接入总结

      本文链接:https://www.haomeiwen.com/subject/ngxhsxtx.html