Android集成微信支付

我个人将集成微信支付的过程分成4个步骤: 微信官方api文档:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1
1.配置各种信息
2.拼凑预订单信息,访问微信服务器生成预订单,主要是为了得到prepared_id —– 建议在自己的服务器操作
3.根据得到的prepared_id及其他信息进行二次签名,调起微信sdk支付 — 前部分建议在服务器操作,后面部分在app端操作
4.根据回调的支付结果执行不同的逻辑

接下来具体说说各个步骤
1、配置各种信息 如在支付Activity中PayActivity(名字自己定)和微信回调的WXPayEntryActivity(这个类的名字不允许改变,包名固定为你应用的包名+wxapi)功能清单文件配置

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!--微信支付回调界面-->
 
     <intent-filter>
         
         <category android:name= "android.intent.category.DEFAULT" >
         <data android:scheme= "你的应用appid" > <!-- 需修改 -->
     </data></category></action></intent-filter>
</activity>
 
<!--微信支付界面-->
 
     <intent-filter>
         
         <category android:name= "android.intent.category.DEFAULT" >
         <data android:scheme= "你的应用appid" > <!-- 需修改 -->
     </data></category></action></intent-filter>
</activity>

当然,权限什么的就不说了,别忘了哈

2.拼凑预订单信息,访问微信服务器生成预订单,主要是为了得到prepared_id,如下的代码是在客户端生成的实例,如果不需要在客户端操作,请无视
生成预订单有10个必选参数分别为 (注意参数名不能改变,post请求,以xml格式)
appid(应用id)、 mch_id(商户号)、 nonce_str(随机字符串,参照我下面的代码生成)、 sign(签名,参照我下面的代码签名)、 body(商品描述)、
out_trade_no(商户订单号,自己生成,唯一)、 total_fee(总金额,单位:分,不能像支付宝有0.01这种)、 spbill_create_ip(终端ip,参照下面ipv4的获取)、 notify_url(微信支付结果异步通知的地址)、 trade_type(交易类型,app的填APP即可)
官方的参数示例

?
1
2
3
4
5
6
7
8
9
10
11
12
     <xml>
    wx2421b1c4370ec43b</appid>
    支付测试</attach>APP支付测试
    <mch_id> 10000100 </mch_id>
    <nonce_str>1add1a30ac87aa2db72f57a2375d8fec</nonce_str>
    <notify_url>http: //wxpay.weixin.qq.com/pub_v2/pay/notify.v2.php</notify_url>
    <out_trade_no> 1415659990 </out_trade_no>
    <spbill_create_ip> 14.23 . 150.211 </spbill_create_ip>
    <total_fee> 1 </total_fee>
    <trade_type>APP</trade_type>
    <sign>0CB01533B8C1EF103065174F50BCA001</sign>
</xml>

下面是几个参数的生成:
随机字符串的生成方式

?
1
2
3
4
5
6
7
8
/**
  * 生成随机字符串,算法自己考虑
  * @return
  */
private String genNonceStr() {
Random random = new Random();
  return MD5.getMessageDigest(String.valueOf(random.nextInt( 10000 )).getBytes());
}

订单号的生成:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
  * get the out_trade_no for an order. 生成商户订单号,该值在商户端应保持唯一(可自定义格式规范)
  *
  */
private String getWXOutTradeNo() {
     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;
}

ipv4的获取方式:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/**
  * 获取自己手机的ipv4地址
  * @return
  */
public String getIp() {
     try {
 
         for (Enumeration<networkinterface> en = NetworkInterface
 
                 .getNetworkInterfaces(); en.hasMoreElements();) {
 
             NetworkInterface intf = en.nextElement();
 
             for (Enumeration<inetaddress> ipAddr = intf.getInetAddresses(); ipAddr
 
                     .hasMoreElements();) {
 
                 InetAddress inetAddress = ipAddr.nextElement();
                 // ipv4地址
                 if (!inetAddress.isLoopbackAddress()
                         && InetAddressUtils.isIPv4Address(inetAddress
                         .getHostAddress())) {
 
                     Log.e( "TAG" , "ipv4=" + inetAddress.getHostAddress());
                     return inetAddress.getHostAddress();
 
                 }
 
             }
 
         }
 
     } catch (Exception ex) {
 
     }
 
     return "192.168.1.0" ;
 
}</inetaddress></networkinterface>

签名的生成:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
  * 生成package参数,就是签名参数
  * @param params
  * @return
  */
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(Constants.API_KEY); // 注意:不能hardcode在客户端,建议genPackage这个过程都由服务器端完成
 
     return MD5.getMessageDigest(sb.toString().getBytes()).toUpperCase();
}</namevaluepair>

生成xml字符串的方法:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
  * 生成xml文件字符串
  * @param packageParams
  * @return
  */
private String toXml(List<namevaluepair> packageParams) {
     StringBuffer xml = new StringBuffer( "" );
     xml.append( "<xml>" );
     for ( int i = 0 ;i < packageParams.size();i++){
         NameValuePair nameValuePair = packageParams.get(i);
         xml.append( "<" + nameValuePair.getName() + ">" + nameValuePair.getValue() + "<!--" + nameValuePair.getName() + "-->" );
         if (i == packageParams.size() - 1 ){
             xml.append( "</xml>" );
         }
     }
     return new String(xml);
}</namevaluepair>

接下来就可以将上面的10个参数生成xml文件字符串上传

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/**
      * 生成预订单的信息
      * @return
      */
     private String getProductArgs(){
 
             String nonceStr = genNonceStr(); //随机字符串
 
             List<namevaluepair> packageParams = new LinkedList<namevaluepair>();
             packageParams
                     .add( new BasicNameValuePair( "appid" , Constants.APP_ID));
 
             packageParams.add( new BasicNameValuePair( "body" , aliOrderBO.getName())); //商品名称
//            packageParams.add(new BasicNameValuePair("input_charset", "UTF-8"));
             packageParams.add( new BasicNameValuePair( "detail" , aliOrderBO.getDescribe())); //商品详情
             packageParams
                     .add( new BasicNameValuePair( "mch_id" , Constants.MCH_ID)); //商户号
             packageParams.add( new BasicNameValuePair( "nonce_str" , nonceStr)); //随机字符串
 
             packageParams.add( new BasicNameValuePair( "notify_url" , UrlStrings.getUrl(UrlIds.WXPAY_NOTIFY))); //接收服务器异步结果的url
 
             String orderCode = getWXOutTradeNo(); //生成商户订单号
             ((MyApplication) getApplication()).setWxTradeNo(orderCode); //将订单号保存
 
             packageParams.add( new BasicNameValuePair( "out_trade_no" , //商户订单号
                     orderCode));
             packageParams.add( new BasicNameValuePair( "spbill_create_ip" ,getIp())); //用户终端IP
             double totalFee = aliOrderBO.getPrice()* 100 ; //价格,单位是分
 
             packageParams.add( new BasicNameValuePair( "total_fee" , String.valueOf(( int )totalFee))); //订单总金额
             packageParams.add( new BasicNameValuePair( "trade_type" , "APP" )); //交易类型
 
             String packageValue = genPackage(packageParams);
 
             packageParams.add( new BasicNameValuePair( "sign" , packageValue));
         try {
             return new String(toXml(packageParams).toString().getBytes(), "ISO8859-1" );
         } catch (UnsupportedEncodingException e) {
             e.printStackTrace();
         }
         return null ;
     }</namevaluepair></namevaluepair>

上传预订单信息给微信服务器

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
String url = String.format( "https://api.mch.weixin.qq.com/pay/unifiedorder" );//微信预订单的地址,请参考微信官方
 
new GetPrepayIdTask().execute(url, entry); //访问微信服务器
 
//上传的类
private class GetPrepayIdTask extends AsyncTask<string, preparewxpaybean= "" > {
 
      @Override
      protected void onPreExecute() {
 
      }
 
      @Override
      protected void onPostExecute(PrepareWXPayBean result) { //这个PrepareWXPayBean是服务器返回的结果,自定义的类
 
          if (result.localRetCode == PrepareWXPayBean.LocalRetCode.ERR_OK) {
 
              sendPayReq(result);
          } else {
 
              Log.e( "TAG" , "error" );
          }
      }
 
      @Override
      protected void onCancelled() {
          super .onCancelled();
      }
 
      @Override
      protected PrepareWXPayBean doInBackground(String... params) {
          PrepareWXPayBean result = new PrepareWXPayBean();
 
          if (params == null || params.length <= 0 ) {
              result.localRetCode = PrepareWXPayBean.LocalRetCode.ERR_ARGU;
              return result;
          }
 
          byte [] buf = Util.httpPost(params[ 0 ],params[ 1 ]);
          if (buf == null || buf.length == 0 ) {
              result.localRetCode = PrepareWXPayBean.LocalRetCode.ERR_HTTP;
              return result;
          }
 
          String content = new String(buf);
 
          result = decodeXML(content);
 
          result.localRetCode = PrepareWXPayBean.LocalRetCode.ERR_OK;
          return result;
      }
  }
</string,>

3.解析返回的预订单信息再次签名,然后调起微信sdk(前面建议的服务器完成,如果不需要,请无视)
返回结果示例(请求成功的结果,其他情况请参考api):

?
1
2
3
4
5
6
7
8
9
10
11
<xml>
    <return_code><!--[CDATA[SUCCESS]]--></return_code>
    <return_msg><!--[CDATA[OK]]--></return_msg>
    <!--[CDATA[wx2421b1c4370ec43b]]--></appid>
    <mch_id><!--[CDATA[ 10000100 ]]--></mch_id>
    <nonce_str><!--[CDATA[IITRi8Iabbblz1Jc]]--></nonce_str>
    <sign><!--[CDATA[7921E432F65EB8ED0CE9755F0E86D72F]]--></sign>
    <result_code><!--[CDATA[SUCCESS]]--></result_code>
    <prepay_id><!--[CDATA[wx201411101639507cbf6ffd8b0779950874]]--></prepay_id>
    <trade_type><!--[CDATA[APP]]--></trade_type>
</xml>

解析结果的方法(这只是个示例,具体方式请自行考虑):

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/**
  * 解析xml
  * @param content
  * @return
  */
private PrepareWXPayBean decodeXML(String content) {
     XmlPullParser parser = Xml.newPullParser();
     PrepareWXPayBean bean = new PrepareWXPayBean();
     try {
         parser.setInput( new StringReader(content));
         int event = parser.getEventType();
         while (event != XmlPullParser.END_DOCUMENT) {
             switch (event) {
                 case XmlPullParser.START_DOCUMENT:
                     break ;
                 case XmlPullParser.START_TAG:
                     if ( "return_code" .equals(parser.getName())) {
                         parser.next();
                         bean.setReturn_code(parser.getText());
                     } else if ( "return_msg" .equals(parser.getName())) {
                         parser.next();
                         bean.setReturn_msg(parser.getText());
                     } else if ( "appid" .equals(parser.getName())) {
                         parser.next();
                         bean.setAppid(parser.getText());
                     } else if ( "mch_id" .equals(parser.getName())) {
                         parser.next();
                         bean.setMch_id(parser.getText());
                     } else if ( "nonce_str" .equals(parser.getName())) {
                         parser.next();
                         bean.setNonce_str(parser.getText());
                     } else if ( "sign" .equals(parser.getName())) {
                         parser.next();
                         bean.setSign(parser.getText());
                     } else if ( "result_code" .equals(parser.getName())) {
                         parser.next();
                         bean.setResult_code(parser.getText());
                     } else if ( "prepay_id" .equals(parser.getName())) {
                         parser.next();
                         bean.setPrepay_id(parser.getText());
                     } else if ( "trade_type" .equals(parser.getName())) {
                         parser.next();
                         bean.setTrade_type(parser.getText());
                     }
                     break ;
                 case XmlPullParser.END_TAG:
                     break ;
             }
             event = parser.next();
         }
     } catch (Exception e) {
         e.printStackTrace();
     }
     return bean;
}

解析完成后进行二次签名,签名方法如下 key就是api秘钥 key设置路径:微信商户平台(pay.weixin.qq.com)–>账户设置–>API安全–>密钥设置

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
private static final char SPLIT = '&' ;
/**
  * 微信开放平台和商户约定的密钥
  *
  * 注意:不能hardcode在客户端,建议genSign这个过程由服务器端完成
  */
 
private String genSign(PayReq req) {
     StringBuilder sb = new StringBuilder();
     sb.append( "appid=" );
     sb.append(req.appId);
     sb.append(SPLIT);
 
     sb.append( "noncestr=" );
     sb.append(req.nonceStr);
     sb.append(SPLIT);
 
     sb.append( "package=" );
     sb.append(req.packageValue);
     sb.append(SPLIT);
 
     sb.append( "partnerid=" );
     sb.append(req.partnerId);
     sb.append(SPLIT);
 
     sb.append( "prepayid=" );
     sb.append(req.prepayId);
     sb.append(SPLIT);
 
     sb.append( "timestamp=" );
     sb.append(req.timeStamp);
     sb.append(SPLIT);
 
     sb.append( "key=" );
     sb.append(Constants.API_KEY); // 注意:不能hardcode在客户端,建议genSign这个过程都由服务器端完成
 
     return MD5.getMessageDigest(sb.toString().getBytes()).toUpperCase();
}

生成时间戳的方法:

?
1
2
3
4
5
6
7
/**
  * 生成时间戳
  * @return
  */
private String genTimeStamp() {
     return String.valueOf(System.currentTimeMillis() / 1000 );
}

然后就可以调起微信sdk支付啦

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
private void sendPayReq(PrepareWXPayBean result) {
     if ( "FAIL" .equals(result.getResult_code())) {
         Log.e(TAG, "sendPayReq fail, retCode = " + result.getResult_code() + ", retmsg = " + result.getReturn_msg());
         return ;
     }
 
     String prepare_id = result.getPrepay_id();
     Log.d(TAG, "sendPayReq, prepare_id = " + prepare_id + ", sign = " + result.getSign());
 
     if (prepare_id == null || prepare_id.length() == 0 ) {
         Log.e(TAG, "sendPayReq fail, prepare_id is empty" );
         return ;
     }
 
     PayReq req = new PayReq();
     req.appId = result.getAppid();
     req.partnerId = result.getMch_id(); //商户号
     req.prepayId = prepare_id;
     req.nonceStr = result.getNonce_str(); //随机字符串
     req.timeStamp = genTimeStamp(); //时间戳
     req.packageValue = "Sign=WXPay" ; //固定字符串
     req.sign = genSign(req); //签名
     //req.extData = "app data"; // optional,微信不处理该字段,会在PayResp结构体中回传该字段
 
     // 在支付之前,如果应用没有注册到微信,应该先调用IWXMsg.registerApp将应用注册到微信
     api.registerApp(Constants.APP_ID);
     api.sendReq(req);
}

4.处理回调 再次强调这个类 微信回调的WXPayEntryActivity(这个类的名字不允许改变,包名固定为你应用的包名+wxapi

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Override
    public void onResp(BaseResp baseResp) {
        Log.d( "TAG" , "onPayFinish, errCode = " + baseResp.errCode);
        //Log.e("TAG", "info=" + baseResp.errStr + ",transaction=" + baseResp.transaction + ",openId=" + baseResp.openId);
        Bundle bundle = new Bundle();
        if (baseResp.getType() == ConstantsAPI.COMMAND_PAY_BY_WX) {
            if (baseResp.errCode == 0 ){
                Toast.makeText(WXPayEntryActivity. this , "支付成功" , Toast.LENGTH_SHORT).show();
 
 
            } else {
                Toast.makeText(WXPayEntryActivity. this , "支付失败" , Toast.LENGTH_SHORT).show();
            }
 
        }

最后再强调一个,微信sdk想要成功调起,必须用正式签名,正式签名,正式签名。不只是支付sdk,分享和登录的也是一样

大体的流程就这样了,小弟能力有限,如果有什么错误欢迎指正!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值