最近开发的微信公众号需要开启公众号支付功能,经过几天时间的开发,终于搞定了,在这里记录一下开发流程,也给需要的朋友提供一点点帮助。
前面的公众号设置我就不多说了,直接进入代码开发。
1、在微信公众号支付的开发文档中,H5调起支付API页面有说明,需要在微信浏览器的内置对象WeixinJSBridge中调用getBrandWCPayRequest方法,该方法需要的参数为:
公众号id | appId | 是 | String(16) | wx8888888888888888 | 商户注册具有支付权限的公众号成功后即可获得 |
时间戳 | timeStamp | 是 | String(32) | 1414561699 | 当前的时间,其他详见时间戳规则 |
随机字符串 | nonceStr | 是 | String(32) | 5K8264ILTKCH16CQ2502SI8ZNMTM67VS | 随机字符串,不长于32位。推荐随机数生成算法 |
订单详情扩展字符串 | package | 是 | String(128) | prepay_id=123456789 | 统一下单接口返回的prepay_id参数值,提交格式如:prepay_id=*** |
签名方式 | signType | 是 | String(32) | MD5 | 签名算法,暂支持MD5 |
签名 | paySign | 是 | String(64) | C380BEC2BFD727A4B6845133519F3AD6 | 签名,详见签名生成算法 |
2、获取订单详情扩展字符串
获取订单详情扩展字符串其实就是要获取prepay_id,在微信公众号支付的开发文档的API列表->统一下单页面有获取prepay_id的参数列表,如下为获取prepay_id的方法:
/**
* 获取prepay_id
* @return AccessToken
* @author ****
* @date 2015.10.12
*/
@SuppressWarnings("null")
public static String getPrepayId(String userid, String addrip)
{
//公众账号ID
String appid = Config.APPID;
//商户号
String mch_id = "**********";
//设备号
String device_info = "WEB";
//随机字符串
String nonce_str = String.valueOf(Math.random());
//商品描述
String body = "1yuanmiaosha";
//商品详情
String detail = "miaosha";
//附加数据
String attach = "detail";
//商户订单号
String out_trade_no = "**************************";
//货币类型
String fee_type = "CNY";
//总金额
int total_fee = 1;
//终端IP
String spbill_create_ip = addrip;
//交易起始时间(供测试用)
String time_start = DateUtil.getNowTime().replace("-", "").replace(" ", "").replace(":", "");
//交易结束时间(供测试用)
String time_expire = String.valueOf(Long.valueOf(time_start)+(long)320);
//商品标记(供测试用)
String goods_tag = "****";
//通知地址(供测试用)
String notify_url ="http://www.baidu.com";
//交易类型
String trade_type ="JSAPI";
//商品ID(供测试用)
String product_id = "12235413214070356458058";
//指定支付方式
String limit_pay = "no_credit";
//用户标识
String openid = userid;
//签名(最后获取)
String []array = {"appid=".concat(appid), "mch_id=".concat(mch_id), "device_info=".concat(device_info),
"nonce_str=".concat(nonce_str), "body=".concat(body), "detail=".concat(detail),
"attach=".concat(attach), "out_trade_no=".concat(out_trade_no), "fee_type=".concat(fee_type),
"total_fee=".concat(String.valueOf(total_fee)),
"spbill_create_ip=".concat(spbill_create_ip), "time_start=".concat(time_start), "time_expire=".concat(time_expire),
"goods_tag=".concat(goods_tag), "notify_url=".concat(notify_url), "trade_type=".concat(trade_type),
"product_id=".concat(product_id), "limit_pay=".concat(limit_pay), "openid=".concat(openid)};
//对数组进行字典排序
SignUtil.sort(array);
//组合成stringA
String stringA = new String() ;
String stringSignTemp = new String();
String sign = new String();
String xmlStr = new String();
String prepay_id = new String();
String key = "***************************************";
for(int i = 0 ; i < array.length ; i++)
{
stringA = stringA.concat(array[i].concat("&"));
}
stringSignTemp = stringA.concat("key=").concat(key);
//对stringSignTemp进行MD5加密
MySecurity mySecurity = new MySecurity();
sign = mySecurity.encode(stringSignTemp, "MD5");
//将sign所有的字母换成大写 得到sign
sign = sign.toUpperCase();
System.out.println("sign 为"+sign);
Map<String, String> paraMap = new HashMap<String, String>();
paraMap.put("appid", appid);
paraMap.put("attach", attach);
paraMap.put("body", body);
paraMap.put("detail", detail);
paraMap.put("device_info", device_info);
paraMap.put("fee_type", fee_type);
paraMap.put("goods_tag", goods_tag);
paraMap.put("limit_pay", limit_pay);
paraMap.put("mch_id", mch_id);
paraMap.put("nonce_str", nonce_str);
paraMap.put("notify_url", notify_url);
paraMap.put("openid", openid);
paraMap.put("out_trade_no", out_trade_no);
paraMap.put("product_id", product_id);
paraMap.put("spbill_create_ip", spbill_create_ip);
paraMap.put("time_expire", time_expire);
paraMap.put("time_start", time_start);
paraMap.put("total_fee", String.valueOf(total_fee));
paraMap.put("trade_type", trade_type);
paraMap.put("sign", sign);
String requestUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";
String xml = ArrayToXml(paraMap);
//System.out.println(" xml 为"+ xml);
xmlStr = HttpUtil.sendHttpsPOST(requestUrl, xml);
//转换编码格式
String start = "<prepay_id><![CDATA[";
String end = "]]></prepay_id>";
prepay_id = xmlStr.substring(xmlStr.indexOf(start)+start.length(), xmlStr.indexOf(end));
return prepay_id;
}
//用于字典排序
public static void sort(String a[]) {
for (int i = 0; i < a.length - 1; i++) {
for (int j = i + 1; j < a.length; j++) {
if (a[j].compareTo(a[i]) < 0) {
String temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
}
}
public String encode(String strSrc, String encodeType) {
MessageDigest md = null;
String strDes = null;
byte[] bt = strSrc.getBytes();
try {
if (encodeType == null || "".equals(encodeType))
encodeType = MD5;//默认使用MD5
md = MessageDigest.getInstance(encodeType);
md.update(bt);
strDes = bytes2Hex(md.digest());
} catch (NoSuchAlgorithmException e) {
return strSrc;
}
return strDes;
}
/**
* map转成xml
*
* @param arr
* @return
*/
public static String ArrayToXml(Map<String, String> arr) {
String xml = "<xml>";
Iterator<Entry<String, String>> iter = arr.entrySet().iterator();
while (iter.hasNext()) {
Entry<String, String> entry = iter.next();
String key = entry.getKey();
String val = entry.getValue();
xml += "<" + key + ">" + val + "</" + key + ">";
}
xml += "</xml>";
return xml;
}
public static String sendHttpsPOST(String url, String data) {
String result = null;
try {
// 设置SSLContext
SSLContext sslcontext = SSLContext.getInstance("TLS");
sslcontext.init(null, new TrustManager[] { myX509TrustManager },
null);
// 打开连接
// 要发送的POST请求url?Key=Value&Key2=Value2&Key3=Value3的形式
URL requestUrl = new URL(url);
HttpsURLConnection httpsConn = (HttpsURLConnection) requestUrl
.openConnection();
// 设置套接工厂
httpsConn.setSSLSocketFactory(sslcontext.getSocketFactory());
// 加入数据
httpsConn.setRequestMethod("POST");
httpsConn.setDoOutput(true);
OutputStream out = httpsConn.getOutputStream() ;
if (data != null)
out.write(data.getBytes("UTF-8"));
out.flush();
out.close();
// 获取输入流
BufferedReader in = new BufferedReader(new InputStreamReader(
httpsConn.getInputStream()));
int code = httpsConn.getResponseCode();
if (HttpsURLConnection.HTTP_OK == code) {
String temp = in.readLine();
/* 连接成一个字符串 */
while (temp != null) {
if (result != null)
result += temp;
else
result = temp;
temp = in.readLine();
}
}
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (ProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
至此已经获取到了prepay_id参数。
3、获取appId、timeStamp、nonceStr、package、signType、paySign参数传至JS页面
/**
* 获取appId、timeStamp、nonceStr、package、signType、paySign
* @param userinfo
* @return string
*/
public String getParameter()
{
BrandWCPayParameter brandWCPayParameter = new BrandWCPayParameter();
String key = "************************************";
//从页面将用户openid传入
String userid = this.getRequest().getParameter("userid");
String addrip = this.getRequest().getParameter("addrip");
//appId
String appId = Config.APPID;
//timeStamp
String timeStamp = String.valueOf(System.currentTimeMillis()/1000);
//nonceStr随机字符串
String nonceStr=String.valueOf(Math.random());
String signType = "MD5";
//package订单详情扩展字符串 prepay_id
String prepay_id = new String();
prepay_id = HttpUtil.getPrepayId(userid,addrip);
String packAge = "prepay_id=" + prepay_id;
//获取签名
String []array = {"appId=".concat(appId), "timeStamp=".concat(timeStamp), "nonceStr=".concat(nonceStr),
"package=".concat(packAge), "signType=".concat(signType)};
SignUtil.sort(array);
//组合成stringA
String stringA = new String() ;
String stringSignTemp = new String();
String paySign = new String();
for(int i = 0 ; i < array.length ; i++)
{
stringA = stringA.concat(array[i].concat("&"));
}
stringSignTemp = stringA.concat("key=").concat(key);
MySecurity mySecurity = new MySecurity();
paySign = mySecurity.encode(stringSignTemp, "MD5");
//将sign所有的字母换成大写 得到sign
paySign = paySign.toUpperCase();
brandWCPayParameter.setAppId(appId);
brandWCPayParameter.setNonceStr(nonceStr);
brandWCPayParameter.setPackAge(packAge);
brandWCPayParameter.setPaySign(paySign);
brandWCPayParameter.setSignType(signType);
brandWCPayParameter.setTimeStamp(timeStamp);
List<BrandWCPayParameter> list = new ArrayList<BrandWCPayParameter>();
list.add(brandWCPayParameter);
JsonConfig jsonConfig = JsonUtil.configJson("yyyy-MM-dd HH:mm:ss");
JSONArray json = JSONArray.fromObject(list, jsonConfig);
this.showjsondata(json.toString());
return null;
}
4、页面调用H5支付API
function onBridgeReady(){
//获取appId、timeStamp、nonceStr、package、signType、paySign
var addrip = "<%=addrip%>";
var userid = "<%=user%>";
var appId;
var timeStamp;
var nonceStr;
var signType;
var packAge;
var paySign;
$.post('/weixin/cj/Cjaction!getParameter.action',{addrip:addrip,userid:userid},function(data)
{
var brandWCPayParameter =data[0];
appId = brandWCPayParameter.appId;
timeStamp = brandWCPayParameter.timeStamp;
nonceStr = brandWCPayParameter.nonceStr;
signType = brandWCPayParameter.signType;
packAge = brandWCPayParameter.packAge;
paySign = brandWCPayParameter.paySign;
alert(paySign);
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId" : appId, //公众号名称,由商户传入
"timeStamp": timeStamp, //时间戳,自1970年以来的秒数
"nonceStr" : nonceStr, //随机串
"package" : packAge,
"signType" : signType, //微信签名方式:
"paySign" : paySign //微信签名
},
function(res){
if(res.err_msg == "get_brand_wcpay_request:ok" ) {
alert("支付成功");
}
}
);
},"json");
}
//微信支付方法(点击按键调用)
function pay(){
if (typeof WeixinJSBridge == "undefined"){
if( document.addEventListener ){
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
}else if (document.attachEvent){
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
}else{
onBridgeReady();
}
}
5、注意事项
获取prepay_id参数时,当商品描述、商品详情、附加数据三个参数为中文时会返回乱码或者签名错误,应该是编码格式的原因,但是具体是什么原因还不是很清楚,如有遇到相同情况的朋友且已解决还请告知;
一定要注意参数的大小写,本人由于大小写原因导致签名失败而浪费了很多时间;
由于急于调通支付功能,代码中存在一些不合理的地方还待优化,请自行甄别;
上述代码中部分方法定义于不同的类中,请自行组织不同类的方法。
6,如果上述材料能对您有任何帮助那么我倍感欣慰,下面是我开发的公众号二维码,有兴趣的朋友可以关注一下!!谢谢!!