Android微信支付V3版
由于公司需求做微信APP支付,在集成过程中也遇到各种问题,比如说签名错误,body编码必须为UTF-8、APP端无法调用支付页面直接到支付结果页面、结果为null,code=-1等等;
1、签名错误问题,首先得确保APPID、商户ID、api密钥正确,其次就是编码问题了,Java文件得是UTF-8的,再有就是接下来代码里注释的。
2、body编码必须为UTF-8的问题,改body编码后报签名错误,唉!各种无奈,于是想到是不是跟开发环境编码有关,换了台电脑,居然成了。(这是我的办法,不一定适用大家)
3、APP端无法调用支付页面直接到支付结果页面,支付结果:null,code=-1,这个问题官方(http://kf.qq.com/faq/140225MveaUz150413eimyiQ.html)给的解决方法是:
开放平台配置的报名和应用签名是否一致:(android);确认是否使用正式的keystore打包apk并安装调试;(android);提交订单部分需要在服务器端完成。
所以,调试微信支付要么申请一个debug.keystore版的,要么使用正式的keystore打包apk并安装调试。
接下来给大家分享我的结果,我是从服务段获取的prepay_id:
服务端:WXPaynewBiz.java
1 import java.io.UnsupportedEncodingException; 2 import java.util.ArrayList; 3 import java.util.List; 4 import java.util.UUID; 5 6 import org.apache.commons.httpclient.NameValuePair; 7 8 import com.jing.lang.MD5Util; 9 public class WXPaynewBiz { 10 /** APP_ID 应用从官方网站申请到的合法appId */ 11 public static final String WX_APP_ID = "APP_ID"; 12 /** 商户号 */ 13 public static final String WX_PARTNER_ID = "商户号"; 14 /** 接口链接 */ 15 public static final String url="https://api.mch.weixin.qq.com/pay/unifiedorder"; 16 /** 商户平台和开发平台约定的API密钥,在商户平台设置 */ 17 public static final String key="API密钥"; 18 19 public void submitOrder(double realPayPrice, int tradeType, String ip) { 20 int realpayPrice=(int)(realPayPrice*100); 21 List<NameValuePair> nvps = new ArrayList <NameValuePair>(); 22 nvps.add(new NameValuePair("appid", WX_APP_ID)); 23 nvps.add(new NameValuePair("body", "商品描述")); 24 nvps.add(new NameValuePair("mch_id", WX_PARTNER_ID)); 25 nvps.add(new NameValuePair("nonce_str", UUID.randomUUID().toString().replace("-", ""))); 26 nvps.add(new NameValuePair("notify_url", "回调通知地址")); 27 nvps.add(new NameValuePair("out_trade_no", "86")); 28 nvps.add(new NameValuePair("spbill_create_ip", ip)); 29 nvps.add(new NameValuePair("total_fee",realpayPrice+"" )); 30 nvps.add(new NameValuePair("trade_type", "APP")); 31 StringBuffer sb=new StringBuffer(); 32 33 for (NameValuePair nvp : nvps) { 34 sb.append(nvp.getName()+"="+nvp.getValue()+"&"); 35 } 36 String signA=sb.toString(); 37 String stringSignTemp=signA+"key="+key ; 38 System.out.println(signA); 39 System.out.println(stringSignTemp); 40 String sign=MD5Util.getMD5String(stringSignTemp).toUpperCase(); 41 System.out.println(sign); 42 nvps.add(new NameValuePair("sign", sign)); 43 try { 44 // 最关键的一步,我们要把最终发送的数据字符转为字节后,再使用“ISO8859-1”进行编码,得到“ISO8859-1”的字符串,否则有可能有“签名错误”的问题 45 byte[] buf = Util.httpPost(url, new String(toXml(nvps).getBytes(),"ISO8859-1")); 46 String content = new String(buf); 47 System.out.println(content); 48 } catch (UnsupportedEncodingException e) { 49 e.printStackTrace(); 50 } 51 } 52 53 private String toXml(List<NameValuePair> params) { 54 StringBuilder sb = new StringBuilder(); 55 sb.append("<?xml version='1.0' encoding='UTF-8'?><xml>"); 56 for (int i = 0; i < params.size(); i++) { 57 sb.append("<"+params.get(i).getName()+">"); 58 59 60 sb.append(params.get(i).getValue()); 61 sb.append("</"+params.get(i).getName()+">"); 62 } 63 sb.append("</xml>"); 64 System.out.println(sb.toString()); 65 return sb.toString(); 66 } 67 public static void main(String[] args) { 68 WXPaynewBiz wx=new WXPaynewBiz(); 69 wx.submitOrder(10.00, 1, "127.0.0.1"); 70 } 71 }
Android端,android端是根据官方给的Demo做的:
一、在自己的项目的包路径中实现WXPayEntryActivity类,即添加 .wxapi.WXPayEntryActivity,在WXPayEntryActivity类中实现onResp函数,支付完成后,微信APP会返回到商户APP并回调onResp函数,开发者需要在该函数中接收通知,判断返回错误码,如果支付成功则去后台查询支付结果再展示用户实际支付结果。注意一定不能以客户端返回作为用户支付的结果,应以服务器端的接收的支付通知或查询API返回的结果为准。代码示例如下:
1 import android.app.Activity; 2 import android.app.AlertDialog; 3 import android.content.DialogInterface; 4 import android.content.DialogInterface.OnClickListener; 5 import android.content.Intent; 6 import android.os.Bundle; 7 8 import com.tencent.mm.sdk.constants.ConstantsAPI; 9 import com.tencent.mm.sdk.modelbase.BaseReq; 10 import com.tencent.mm.sdk.modelbase.BaseResp; 11 import com.tencent.mm.sdk.openapi.IWXAPI; 12 import com.tencent.mm.sdk.openapi.IWXAPIEventHandler; 13 import com.tencent.mm.sdk.openapi.WXAPIFactory; 14 15 public class WXPayEntryActivity extends Activity implements IWXAPIEventHandler { 16 17 private IWXAPI api; 18 19 @Override 20 public void onCreate(Bundle savedInstanceState) { 21 super.onCreate(savedInstanceState); 22 setContentView(R.layout.pay_result); 23 api = WXAPIFactory.createWXAPI(this, Constants.WX_APP_ID); 24 api.handleIntent(getIntent(), this); 25 } 26 27 @Override 28 protected void onNewIntent(Intent intent) { 29 super.onNewIntent(intent); 30 setIntent(intent); 31 api.handleIntent(intent, this); 32 } 33 34 @Override 35 public void onReq(BaseReq req) { 36 } 37 38 @Override 39 public void onResp(BaseResp resp) { 40 if (resp.getType() == ConstantsAPI.COMMAND_PAY_BY_WX) { 41 int code = resp.errCode; 42 String msg = ""; 43 switch (code) { 44 case 0: 45 msg = "支付成功!"; 46 break; 47 case -1: 48 msg = "支付失败!"; 49 break; 50 case -2: 51 msg = "您取消了支付!"; 52 break; 53 54 default: 55 msg = "支付失败!"; 56 break; 57 } 58 AlertDialog.Builder builder = new AlertDialog.Builder(this); 59 builder.setTitle(R.string.app_tip); 60 builder.setMessage(msg); 61 builder.setNegativeButton("确定", new OnClickListener() { 62 @Override 63 public void onClick(DialogInterface dialog, int which) { 64 dialog.dismiss(); 65 WXPayEntryActivity.this.finish(); 66 } 67 }); 68 builder.show(); 69 } 70 } 71 }
二、在APP项目配置文件AndroidManifest.xml中配置WXPayEntryActivity,代码如下:
<activity android:name=".wxapi.WXPayEntryActivity" android:exported="true" android:launchMode="singleTop"/>
三、看官方demo还有一个AppRegister.java文件,具体不知道什么作用,我也给配置了,代码如下:
import com.tencent.mm.sdk.openapi.IWXAPI; import com.tencent.mm.sdk.openapi.WXAPIFactory; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; public class AppRegister extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { final IWXAPI msgApi = WXAPIFactory.createWXAPI(context, null); // 将该app注册到微信 msgApi.registerApp(Constants.WX_APP_ID); } }
1 <receiver 2 android:name=".wxpay.AppRegister"> 3 <intent-filter> 4 <action android:name="com.tencent.mm.plugin.openapi.Intent.ACTION_REFRESH_WXAPP" /> 5 </intent-filter> 6 </receiver>
四、在项目包名下添加微信客户端回调函数类 .wxapi.WXEntryActivity,并添加AndroidManifest.xml配置:代码如下:
1 import android.content.Intent; 2 import android.widget.Toast; 3 import cn.sharesdk.wechat.utils.WXAppExtendObject; 4 import cn.sharesdk.wechat.utils.WXMediaMessage; 5 import cn.sharesdk.wechat.utils.WechatHandlerActivity; 6 7 /** 微信客户端回调activity示例 */ 8 public class WXEntryActivity extends WechatHandlerActivity { 9 10 /** 11 * 处理微信发出的向第三方应用请求app message 12 * <p> 13 * 在微信客户端中的聊天页面有“添加工具”,可以将本应用的图标添加到其中 14 * 此后点击图标,下面的代码会被执行。Demo仅仅只是打开自己而已,但你可 15 * 做点其他的事情,包括根本不打开任何页面 16 */ 17 public void onGetMessageFromWXReq(WXMediaMessage msg) { 18 Intent iLaunchMyself = getPackageManager().getLaunchIntentForPackage(getPackageName()); 19 startActivity(iLaunchMyself); 20 } 21 22 /** 23 * 处理微信向第三方应用发起的消息 24 * <p> 25 * 此处用来接收从微信发送过来的消息,比方说本demo在wechatpage里面分享 26 * 应用时可以不分享应用文件,而分享一段应用的自定义信息。接受方的微信 27 * 客户端会通过这个方法,将这个信息发送回接收方手机上的本demo中,当作 28 * 回调。 29 * <p> 30 * 本Demo只是将信息展示出来,但你可做点其他的事情,而不仅仅只是Toast 31 */ 32 public void onShowMessageFromWXReq(WXMediaMessage msg) { 33 if (msg != null && msg.mediaObject != null 34 && (msg.mediaObject instanceof WXAppExtendObject)) { 35 WXAppExtendObject obj = (WXAppExtendObject) msg.mediaObject; 36 Toast.makeText(this, obj.extInfo, Toast.LENGTH_SHORT).show(); 37 } 38 } 39 40 }
<activity android:name=".wxapi.WXEntryActivity" android:theme="@android:style/Theme.Translucent.NoTitleBar" android:configChanges="keyboardHidden|orientation|screenSize" android:exported="true" android:screenOrientation="portrait" />
五、最后做我们的发起微信支付的页面,主要代码如下(主要还是官方Demo里的),另外还要在发起微信支付的Activity标签中添加属性<data android:scheme="appid"/>:
1 private PayReq req; 2 private StringBuffer sb; 3 4 /** 5 * 微信支付临时订单的prepay_id 6 */ 7 private void getWXPrepayId(){ 8 //从服务器获取,代码略…… 9 //获取成功后 10 sendPayReq(prepayId); 11 } 12 13 /** 14 * 调用微信支付页面 15 */ 16 private void sendPayReq(String prepayId){ 17 genPayReq(prepayId); 18 WXapi.registerApp(Constants.WX_APP_ID); 19 WXapi.sendReq(req); 20 try { 21 Thread.sleep(1000*3); 22 } catch (InterruptedException e) { 23 e.printStackTrace(); 24 } 25 closeProDialog(); 26 this.finish(); 27 } 28 29 /** 30 * 生成签名参数 31 */ 32 private void genPayReq(String prepayId) { 33 req.appId = Constants.WX_APP_ID; 34 req.partnerId = Constants.WX_PARTNER_ID; 35 req.prepayId = prepayId; 36 req.packageValue = "Sign=WXPay"; 37 req.nonceStr = genNonceStr(); 38 req.timeStamp = String.valueOf(genTimeStamp()); 39 40 41 List<NameValuePair> signParams = new LinkedList<NameValuePair>(); 42 signParams.add(new BasicNameValuePair("appid", req.appId)); 43 signParams.add(new BasicNameValuePair("noncestr", req.nonceStr)); 44 signParams.add(new BasicNameValuePair("package", req.packageValue)); 45 signParams.add(new BasicNameValuePair("partnerid", req.partnerId)); 46 signParams.add(new BasicNameValuePair("prepayid", req.prepayId)); 47 signParams.add(new BasicNameValuePair("timestamp", req.timeStamp)); 48 req.sign = genAppSign(signParams); 49 sb.append("sign\n"+req.sign+"\n\n"); 50 } 51 52 /** 53 * 随机数 54 * @return 55 */ 56 private String genNonceStr() { 57 Random random = new Random(); 58 return MD5Util.getMessageDigest(String.valueOf(random.nextInt(10000)).getBytes()); 59 } 60 61 /** 62 * 时间戳 63 * @return 64 */ 65 private long genTimeStamp() { 66 return System.currentTimeMillis() / 1000; 67 } 68 69 /** 70 * 获取签名 71 * @param params 72 * @return 73 */ 74 private String genAppSign(List<NameValuePair> params) { 75 StringBuilder sb = new StringBuilder(); 76 77 for (int i = 0; i < params.size(); i++) { 78 sb.append(params.get(i).getName()); 79 sb.append('='); 80 sb.append(params.get(i).getValue()); 81 sb.append('&'); 82 } 83 sb.append("key="); 84 sb.append(Constants.WX_API_SECRET); 85 this.sb.append("sign str\n"+sb.toString()+"\n\n"); 86 String appSign = MD5Util.getMessageDigest(sb.toString().getBytes()).toUpperCase(Locale.CHINA); 87 return appSign; 88 }
1 <activity android:name=".OrderFormPayActivity" > 2 <data android:scheme="自己的appid"/> 3 </activity>
到此就差不多了,主要还是的注意下编码格式,希望对各位有帮助,谢谢!
附官方demo:wechat_sdk_sample_android_v3_pay.zip