1.微信支付的注册申请可用的appId等就不赘述了,网上资料很多不过这里面比较容易陷入坑的地方就是 签名的获取可以使用官方签名工具进行获取默认debug版签名;
2.等待微信开放平台审核通过后,就可以查看appId,appSecret等参数,然后支付功能申请开通,我这已经开通成功
填写审核资料待通过后,微信官方会将一个有商户账号和密码等数据的邮件发送到你填写的联系邮箱里面,利用这个账号密码进行商户平台的登录https://pay.weixin.qq.com/index.php/core/home/login?return_url=%2F;找到账户中心的api安全现在一系列的安全证书等,重点是设置api密钥,利用密钥生成器http://tool.chinaz.com/tools/md5.aspx?qq-pf-to=pcqq.c2c生成32位的随机字母数字组合,按照设置api密钥提示的步骤进行设置生成后要牢记密钥否则只能重置不能查看;这样我们需要的调起微信支付的参数就能够全部获取到了,接下来就是调起微信支付的代码部分了;
(1)去微信开放平台申请微信支付服务,绑定自己的应用这里具体不多讲,但是一定要申请完成,将会得到是三个参数
//appid 微信分配的公众账号ID
public static final String APP_ID = "";
//商户号 微信分配的公众账号ID
public static final String MCH_ID = "";
// API密钥,在商户平台设置
public static final String API_KEY= "";
**坑点提示:在微信开发平台设置包名和签名。这里的包名一定要和你自己的包名一样,就是manifest中的package,签名一定要和你用官方app生成的一样(https://open.weixin.qq.com/zh_CN/htmledition/res/dev/download/sdk/Gen_Signature_Android.apk)。
微信会根据你的填写的包名,然后对你的keystore进行一种算法,生成你的签名。包名和签名一定要和微信开放平台的相同。不过这里需要注意的是,如果你发布的正式版本,需要用官方app重新生成签名,然后在开放平台重新设置sign,因为测试版本的keystore与正式版的keystore不一样。总之,就是你用的keystore生成的sign要和微信开放平台的时刻保持一致。**
(2).准备工作做好了,接下来就是开发了,先下载微信的jar包,导入。
微信支付分为三个步骤
.生成prepayId
@Override
protected Map<String, String> doInBackground(String... params) {
// TODO Auto-generated method stub
String url=String.format(params[0]);
String entity=getProductArgs();
Log.e("Simon",">>>>"+entity);
byte[] buf=Util.httpPost(url, entity);
String content = new String(buf);
Log.e("orion", "----"+content);
Map<String,String> xml=decodeXml(content);
return xml;
}
2.生成签名参数
private void genPayReq() {
req.appId = Constants.APP_ID;
req.partnerId = Constants.MCH_ID;
if (resultunifiedorder!=null) {
req.prepayId = resultunifiedorder.get("prepay_id");
req.packageValue = "prepay_id="+resultunifiedorder.get("prepay_id");
}
else {
Toast.makeText(MainActivity.this, "prepayid为空", Toast.LENGTH_SHORT).show();
}
req.nonceStr = getNonceStr();
req.timeStamp = String.valueOf(genTimeStamp());
List<NameValuePair> signParams = new LinkedList<NameValuePair>();
signParams.add(new BasicNameValuePair("appid", req.appId));
signParams.add(new BasicNameValuePair("noncestr", req.nonceStr));
signParams.add(new BasicNameValuePair("package", req.packageValue));
signParams.add(new BasicNameValuePair("partnerid", req.partnerId));
signParams.add(new BasicNameValuePair("prepayid", req.prepayId));
signParams.add(new BasicNameValuePair("timestamp", req.timeStamp));
req.sign = genAppSign(signParams);
sb.append("sign\n"+req.sign+"\n\n");
textView.setText(sb.toString());
Log.e("Simon", "----"+signParams.toString());
}
.调起支付。
/*
* 调起微信支付
*/
private void sendPayReq() {
msgApi.registerApp(Constants.APP_ID);
msgApi.sendReq(req);
Log.i(">>>>>", req.partnerId);
}
下面给出完整代码
package com.alpha.live;
import java.io.StringReader;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
import org.xmlpull.v1.XmlPullParser;
import com.tencent.mm.sdk.modelpay.PayReq;
import com.tencent.mm.sdk.openapi.IWXAPI;
import com.tencent.mm.sdk.openapi.WXAPIFactory;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.util.Xml;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
/**
* Created by Simon on 2016/12/2.
*/
public class MainActivity extends Activity implements OnClickListener {
private Button submitButton;
private Button confirmButton;
private TextView textView;
private StringBuffer sb;
private Map<String,String> resultunifiedorder;
private PayReq req;
private final IWXAPI msgApi = WXAPIFactory.createWXAPI(this, null);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
submitButton=(Button) findViewById(R.id.bt_submit_order);
confirmButton=(Button) findViewById(R.id.bt_corfirm);
textView=(TextView) findViewById(R.id.tv_prepay_id);
submitButton.setOnClickListener(this);
confirmButton.setOnClickListener(this);
sb=new StringBuffer();
req=new PayReq();
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case R.id.bt_submit_order:
String urlString="https://api.mch.weixin.qq.com/pay/unifiedorder";
PrePayIdAsyncTask prePayIdAsyncTask=new PrePayIdAsyncTask();
prePayIdAsyncTask.execute(urlString); //生成prepayId
break;
case R.id.bt_corfirm:
genPayReq();//生成签名参数
sendPayReq();//调起支付
break;
default:
break;
}
}
/*
* 调起微信支付
*/
private void sendPayReq() {
msgApi.registerApp(Constants.APP_ID);
msgApi.sendReq(req);
Log.i(">>>>>", req.partnerId);
}
private long genTimeStamp() {
return System.currentTimeMillis() / 1000;
}
private void genPayReq() {
req.appId = Constants.APP_ID;
req.partnerId = Constants.MCH_ID;
if (resultunifiedorder!=null) {
req.prepayId = resultunifiedorder.get("prepay_id");
req.packageValue = "prepay_id="+resultunifiedorder.get("prepay_id");
}
else {
Toast.makeText(MainActivity.this, "prepayid为空", Toast.LENGTH_SHORT).show();
}
req.nonceStr = getNonceStr();
req.timeStamp = String.valueOf(genTimeStamp());
List<NameValuePair> signParams = new LinkedList<NameValuePair>();
signParams.add(new BasicNameValuePair("appid", req.appId));
signParams.add(new BasicNameValuePair("noncestr", req.nonceStr));
signParams.add(new BasicNameValuePair("package", req.packageValue));
signParams.add(new BasicNameValuePair("partnerid", req.partnerId));
signParams.add(new BasicNameValuePair("prepayid", req.prepayId));
signParams.add(new BasicNameValuePair("timestamp", req.timeStamp));
req.sign = genAppSign(signParams);
sb.append("sign\n"+req.sign+"\n\n");
textView.setText(sb.toString());
Log.e("Simon", "----"+signParams.toString());
}
private String genAppSign(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);
this.sb.append("sign str\n"+sb.toString()+"\n\n");
String appSign = MD5.getMessageDigest(sb.toString().getBytes());
Log.e("Simon","----"+appSign);
return appSign;
}
private class PrePayIdAsyncTask extends AsyncTask<String,Void, Map<String, String>>
{
private ProgressDialog dialog;
@Override
protected void onPreExecute() {
// TODO Auto-generated method stub
super.onPreExecute();
dialog = ProgressDialog.show(MainActivity.this, "提示", "正在提交订单");
}
@Override
protected Map<String, String> doInBackground(String... params) {
// TODO Auto-generated method stub
String url=String.format(params[0]);
String entity=getProductArgs();
Log.e("Simon",">>>>"+entity);
byte[] buf=Util.httpPost(url, entity);
String content = new String(buf);
Log.e("orion", "----"+content);
Map<String,String> xml=decodeXml(content);
return xml;
}
@Override
protected void onPostExecute(Map<String, String> result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
if (dialog != null) {
dialog.dismiss();
}
sb.append("prepay_id\n"+result.get("prepay_id")+"\n\n");
textView.setText(sb.toString());
resultunifiedorder=result;
}
}
public Map<String,String> decodeXml(String content) {
try {
Map<String, String> xml = new HashMap<String, String>();
XmlPullParser parser = Xml.newPullParser();
parser.setInput(new StringReader(content));
int event = parser.getEventType();
while (event != XmlPullParser.END_DOCUMENT) {
String nodeName=parser.getName();
switch (event) {
case XmlPullParser.START_DOCUMENT:
break;
case XmlPullParser.START_TAG:
if("xml".equals(nodeName)==false){
//实例化student对象
xml.put(nodeName,parser.nextText());
}
break;
case XmlPullParser.END_TAG:
break;
}
event = parser.next();
}
return xml;
} catch (Exception e) {
Log.e("Simon","----"+e.toString());
}
return null;
}
private String getProductArgs() {
// TODO Auto-generated method stub
StringBuffer xml=new StringBuffer();
try {
String nonceStr=getNonceStr();
xml.append("<xml>");
List<NameValuePair> packageParams=new LinkedList<NameValuePair>();
packageParams.add(new BasicNameValuePair("appid",Constants.APP_ID));
packageParams.add(new BasicNameValuePair("body", "APP pay test"));
packageParams.add(new BasicNameValuePair("mch_id", Constants.MCH_ID));
packageParams.add(new BasicNameValuePair("nonce_str", nonceStr));
packageParams.add(new BasicNameValuePair("notify_url", "https://www.baidu.com"));//写你们的回调地址
packageParams.add(new BasicNameValuePair("out_trade_no",genOutTradNo()));
packageParams.add(new BasicNameValuePair("total_fee", "1"));
packageParams.add(new BasicNameValuePair("trade_type", "APP"));
String sign=getPackageSign(packageParams);
packageParams.add(new BasicNameValuePair("sign", sign));
String xmlString=toXml(packageParams);
return xmlString;
} catch (Exception e) {
// TODO: handle exception
return null;
}
}
//生成订单号,测试用,在客户端生成
private String genOutTradNo() {
Random random = new Random();
// return "dasgfsdg1234"; //订单号写死的话只能支付一次,第二次不能生成订单
return MD5.getMessageDigest(String.valueOf(random.nextInt(10000)).getBytes());
}
//生成随机号,防重发
private String getNonceStr() {
// TODO Auto-generated method stub
Random random=new Random();
return MD5.getMessageDigest(String.valueOf(random.nextInt(10000)).getBytes());
}
/**
生成签名
*/
private String getPackageSign(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);
String packageSign = MD5.getMessageDigest(sb.toString().getBytes()).toUpperCase();
Log.e("Simon",">>>>"+packageSign);
return packageSign;
}
/*
* 转换成xml
*/
private String toXml(List<NameValuePair> params) {
StringBuilder sb = new StringBuilder();
sb.append("<xml>");
for (int i = 0; i < params.size(); i++) {
sb.append("<"+params.get(i).getName()+">");
sb.append(params.get(i).getValue());
sb.append("</"+params.get(i).getName()+">");
}
sb.append("</xml>");
Log.e("Simon",">>>>"+sb.toString());
return sb.toString();
}
}
(3)配置文件需要顺便提一下
所需要的权限、微信activiy声明以及广播的
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<activity
android:name=".wxapi.WXPayEntryActivity"
android:exported="true"
android:launchMode="singleTop"/>
<receiver
android:name="simcpux.AppRegister">
<intent-filter>
<action android:name="com.tencent.mm.plugin.openapi.Intent.ACTION_REFRESH_WXAPP" />
</intent-filter>
</receiver>
还有就是你调起支付界面的activity的注册,有的人加上了,我是没有加这部分
<activity android:name=".activity.PayActivity">
<!--<intent-filter>-->
<!--<action android:name="android.intent.action.VIEW"/>-->
<!--<category android:name="android.intent.category.DEFAULT"/>-->
<!--<data android:scheme="appId"/>-->
<!--</intent-filter>-->
</activity>
为了大家可以直接运行这个demo,我的微信加签都是在本地执行的,获取prepayid和加签都应该在服务端完成,还有最终的支付返回结果也是以服务端的为准。
运行你的demo的时候需要注意的几个地方是:替换constants的三个参数、包名的修改,要使用你在微信平台上注册的包名、wxapi这个包一定要有并且里面放置
WXPayEntryAcivity这个类,然后运行基本没有问题;如果没有检查没有问题还是不能调起支付时,尝试清理微信缓存重新安装应用进行调试;到这里就完了那是不可能的
我们使用的只是调试的debug版本在正式上线之前还要对app进行打包签名等步骤;
在Android Studio中,可以使用Gradle进行打包时自动签名。其实Android Studio默认会给调试应用加上Debug签名,但有时候调一些第三方SDK时,需要正式签名才能调起来,所以接下来分享一下使用Gradle自动签名的方法。
一、创建签名文件
打开AS,选择Build->Generate Signed APK,选择要打包的项目,点击Next,再点击Create new...创建签名文件
填写签名文件响应信息,如下所示,Password、Key-Alias、Key-Password这三个值需要记住,然后点击OK,完成创建。
完成之后,在相应路径生成一个jks签名文件。如果选择手动再进行打包,就可以选择该签名文件,然后对应填入密码与别名,进行签名,也可以。
二、配置Gradle自动打包
拷贝签名文件到主工程根目录,打开主工程的build.gradle文件,在android节点下,增加以下内容:
- signingConfigs {
- release {
- storeFile file('keystore.jks')
- storePassword '123456'
- keyAlias 'yyh'
- keyPassword '123456'
- }
- }
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
- signingConfig signingConfigs.release
- }
- debug {
- signingConfig signingConfigs.release
- }
- }
storePassword:密码(刚才填的第一个密码)
keyAlias:别名
keyPassword:别名密码
然后重新build一下工程,展开右边栏Gradle选项卡,如下所示:
assembleRelease,即为正式签名。双击assembleRelease,则会开始执行命令:
下次直接run起来的时候,就带正式签名了。这可比手动签名省事多啦~~
当然,为了提高安全性,我们还可以把那四个属性作为变量配置在主工程的gradle.propreties,然后在build.gradle引用变量就可以了。如下:
这样也能起到同样的效果。待上线后重新下载用签名工具获取签名后修改微信开放平台上的签名保存后,等待大概不知道多长之间就可以正式的使用微信支付功能了;
三、应用程序签名的意义(1)保障开发者的合法权益
在我们对应用程序进行打包签名后,即表示此应用程序是签名人或机构所开发的,对此应用程序具有所有权。
(2)预防应用程序替换
应用程序签名可以防止部分人通过使用相同的Package Name来混淆替换已经安装的程序,从而出现一些恶意篡改。APK如果使用一个key签名,发布时另一个key签名的文件将无法安装或覆盖老的版本,这样可以防止你已安装的应用被恶意的第三方覆盖或替换掉。实际上就是一种标识。
(3)保证应用程序版本的一致性
一般应用程序都会有更新,即版本的升级。如果应用程序的签名不一致,是无法进行更新替代的。所以应用程序的签名是保证当前应用程序顺利进行更新安装的前提。
(4)可以通过权限(permission)的方式在多个程序间共享数据和代码
Android提供了基于数字证书的权限赋予机制,应用程序可以和其他的程序共享概功能或者数据给那那些与自己拥有相同数字证书的程序。如果某个权限(permission)的protectionLevel是signature,则这个权限就只能授予那些跟该权限所在的包拥有同一个数字证书的程序。另一方面,Android系统允许拥有同一个数字签名的程序运行在一个进程中,Android程序会将他们视为同一个程序。所以开发者可以将自己的程序分模块开发,而用户只需要在需要的时候下载适当的模块。