美文网首页编程技术
支付宝、微信,第三方支付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