您的位置:首页 > 移动开发 > 微信开发

微信和支付宝支付实战

2016-08-31 17:43 477 查看
最近的项目中要用到移动支付。在此总结下

1、微信支付 :因为需求是扫码支付即时到账,用的是native方式。按照官方文档的说法,调用“https://api.mch.weixin.qq.com/pay/unifiedorder” 这个接口生成一个预支付的订单,微信会返回给你一个订单的二维码的地址,然后把二维码解析展示出来用户扫码就可以支付了。

    //微信支付

String random = RandomStringGenerator.getRandomStringByLength(32);
Map<String,Object> params = new HashMap<String,Object>();
params.put("appid", TenConfig.getAppid());
params.put("mch_id", TenConfig.getMchid());
params.put("nonce_str", random);
params.put("body", body.toString().trim());
params.put("out_trade_no", order.getOrderNumber());
params.put("total_fee", sump);
params.put("spbill_create_ip", TenConfig.SPBILL_CREATE_IP);
params.put("notify_url", TenConfig.getNotify_url());
params.put("trade_type", "NATIVE");
params.put("product_id", productId);
String sign = TenUtils.getSign(params);
//拼接xml
StringBuilder xml = new StringBuilder("<xml>");
xml.append("<appid>").append("<![CDATA["+TenConfig.getAppid()+"]]>").append("</appid>");
xml.append("<mch_id>").append("<![CDATA["+TenConfig.getMchid()+"]]>").append("</mch_id>");
xml.append("<nonce_str>").append("<![CDATA["+random+"]]>").append("</nonce_str>");
xml.append("<body>").append("<![CDATA["+body.toString().trim()+"]]>").append("</body>");
xml.append("<out_trade_no>").append("<![CDATA["+order.getOrderNumber()+"]]>").append("</out_trade_no>");
xml.append("<total_fee>").append("<![CDATA["+sump+"]]>").append("</total_fee>");
xml.append("<spbill_create_ip>").append("<![CDATA["+TenConfig.SPBILL_CREATE_IP+"]]>").append("</spbill_create_ip>");
xml.append("<notify_url>").append("<![CDATA["+TenConfig.getNotify_url()+"]]>").append("</notify_url>");
xml.append("<trade_type>").append("<![CDATA[NATIVE]]>").append("</trade_type>");
xml.append("<product_id>").append("<![CDATA["+productId+"]]>").append("</product_id>");
xml.append("<sign>").append("<![CDATA["+sign+"]]>").append("</sign>");
xml.append("</xml>");<pre name="code" class="java">  logger.info("XML:\n"+xml.toString());
String resStr = TenUtils.postData(TenConfig.UNIFIED_ORDER, xml.toString(), "text/html;charset=utf-8");


这一步主要是拼接预下单接口的参数,要注意的是请求的要用utf-8编码下,不然可能会出现签名错误之类的返回。

把MD5签名算法也贴上 在这里被编码坑了。

ArrayList<String> list = new ArrayList<String>();
for(Map.Entry<String,Object> entry:map.entrySet()){
if(entry.getValue()!=""){
list.add(entry.getKey() + "=" + entry.getValue() + "&");
}
}
int size = list.size();
String [] arrayToSort = list.toArray(new String[size]);
Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
StringBuilder sb = new StringBuilder();
for(int i = 0; i < size; i ++) {
sb.append(arrayToSort[i]);
}
String result = sb.toString();
result += "key=" + TenConfig.getKey();
result = MD5Encrypt.MD5Encode(result, "utf-8").toUpperCase();
return result;


MD5签名的时候切记要用“utf-8”编码,不然会出现签名错误,这是个深坑,接口文档是没有说明的,

贴上MD5算法

public static String md5(String s)
{
byte[] input = s.getBytes();
String output = null;

char[] hexChar = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f' };
try
{
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(input);

byte[] tmp = md.digest();
char[] str = new char[32];
byte b = 0;
for (int i = 0; i < 16; i++)
{
b = tmp[i];
str[(2 * i)] = hexChar[(b >>> 4 & 0xF)];
str[(2 * i + 1)] = hexChar[(b & 0xF)];
}
output = new String(str);
}
catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
}
return output;
}

private static String byteArrayToHexString(byte[] b)
{
StringBuffer resultSb = new StringBuffer();
for (int i = 0; i < b.length; i++) {
resultSb.append(byteToHexString(b[i]));
}
return resultSb.toString();
}

private static String byteToHexString(byte b)
{
int n = b;
if (n < 0) {
n += 256;
}
int d1 = n / 16;
int d2 = n % 16;
return hexDigits[d1] + hexDigits[d2];
}

public static String MD5Encode(String origin, String charsetname)
{
String resultString = null;
try
{
resultString = new String(origin);
MessageDigest md = MessageDigest.getInstance("MD5");
if ((charsetname == null) || ("".equals(charsetname))) {
resultString = byteArrayToHexString(md.digest(resultString
.getBytes()));
} else {
resultString = byteArrayToHexString(md.digest(resultString
.getBytes(charsetname)));
}
}
catch (Exception localException) {}
return resultString;
}

private static final String[] hexDigits = { "0", "1", "2", "3", "4", "5",
"6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };


然后创建连接去请求微信接口

BufferedReader reader = null;
try {
URL url = new URL(urlStr);
URLConnection conn = url.openConnection();
conn.setDoOutput(true);
conn.setConnectTimeout(CONNECT_TIMEOUT);
conn.setReadTimeout(CONNECT_TIMEOUT);
if(contentType!=null && !"".equals(contentType))
conn.setRequestProperty("content-type", contentType);
OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream(), DEFAULT_ENCODING);
if(data == null)
data = "";
writer.write(data);
writer.flush();
writer.close();

reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), DEFAULT_ENCODING));
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line);
sb.append("\r\n");
}
return sb.toString();
} catch (IOException e) {
logger.error("Error connecting to " + urlStr + ": " + e.getMessage());
} finally {
try {
if (reader != null)
reader.close();
} catch (IOException e) {
}
}
return null;


调用之后会拿到一个xml 里面是返回值。解析xml

Map resMap = XMLParser.getMapFromXML(resStr);

if(resMap == null){
logger.info("统一下单接口调用失败");
}else{
String returnCode = (String) resMap.get("return_code");
if(returnCode.equals("SUCCESS")){
logger.info("统一下单接口调用成功");
String codeUrl = (String) resMap.get("code_url");
//把二维码连接返回给页面
result.put("msg", codeUrl);

//更新订单的支付方式,金额等
order.setPayAmount(Float.parseFloat(sumprice));
order.setPayType(payType);
order.setWxPayNonceStr(random);
order.setBody(body.toString());
if(orderService.updateWhenPay(order) > 0){
result.put("code", "ok");
}else{
result.put("code", "err");
}
}
解析xml把生成的二维码给页面 通过jQuery的qrcode.js把地址生成二维码。

<span style="white-space:pre">	</span>$.post("/xxx/xxxxx.jspx",{"orderId":orderId
,"payType":payType,"sumPrice":price},
function(data){
if(data&&data.code=="ok"){
if(payType==1){
//微信支付
$("#model_content").css("display","block");
jQuery('#qrcodeCanvas').qrcode({
render   : "canvas",//设置渲染方式
width       : 210,     //设置宽度
height      : 210,     //设置高度
typeNumber  : -1,      //计算模式
correctLevel    : QRErrorCorrectLevel.H,//纠错等级
background      : "#ffffff",//背景颜色
foreground      : "#000000", //前景颜色
text	: data.msg
});
intel =setInterval(function(){
//请求查询订单状态
queryOrderStatus(orderId,intel);
},2000);
}else if(payType==2){
//支付宝
$("#ali_pay").html(data.msg);
}
}else{
window.wxc.xcConfirm("支付失败!", window.wxc.xcConfirm.typeEnum.error);
}
});


用qrcode.js的生成二维码的时候render   建议用canvas方式 虽然它还提供了table方式,但是我用的时候table方式无法生成二维码的但是canvas是可以的 ,没弄懂为什么。如果顺利的话 二维码就生成了,扫码就可以支付了。解释下这段js代码,请求成功后用一个遮罩层,把二维码放遮罩层上,然后用一个函数去轮询这个订单的状态,因为调用时异步的吗,如果查询到订单已经支付了的话,清除掉轮询并就跳转到支付完成页面。

下面贴微信回调的代码

try {
//获取微信回调的流
BufferedReader br = new BufferedReader(new InputStreamReader((ServletInputStream)request.getInputStream()));
<span style="white-space:pre">	</span>        String line = null;
<span style="white-space:pre">	</span>StringBuilder sb = new StringBuilder();
<span style="white-space:pre">	</span>        while((line = br.readLine())!=null){
sb.append(line);
}
//把微信返回的xml转换成map
Map result = XMLParser.getMapFromXML(sb.toString());

//返回给微信的xml
StringBuilder reXML = new StringBuilder("<xml>");

//return_code 字段是通信标识,非交易标识,交易是否成功需要查看result_code来判断
if(result.get("return_code").toString().equals("SUCCESS")){
//回调成功
logger.info("微信支付成功,返回的XML:\n"+sb.toString());

//获取微信回调的参数
String orderNum = (String) result.get("out_trade_no");
//业务结果
String resultCode = (String) result.get("result_code");

String transactionId = result.get("transaction_id").toString();

String sign = result.get("sign").toString();
if(result.get("sign")!=null){
//去除Sign
result.remove("sign");
}
if(sign.equals(TenUtils.getSign(result))){
//签名通过,判断交易状态
if(resultCode.equals("SUCCESS")){
if(orderNum !=null && !"".equals(orderNum)){
Order order = orderService.findByOrderNum(orderNum);
if(order != null){
//支付成功
if(order.getStatus() == 0){
//订单如果是待支付状态,更改订单状态为确认中状态(1),更新支付完成时间
orderService.updateStatus(order.getId(), 1);
orderService.updatePayCompleteTime(order.getId());

reXML.append("<return_code><![CDATA[SUCCESS]]></return_code>");
reXML.append("<return_msg><![CDATA[OK]]></return_msg>");

//插入支付日志
Map<String,String> map = new HashMap<String,String>();
map.put("pro_type", "销售");
map.put("pay_type", "微信");
map.put("pro_info", order.getBody());
map.put("user_name", order.getBuyMember().getUsername());
map.put("trade_no", orderNum);
map.put("pay_amount", order.getPayAmount()+"");
map.put("status", "支付完成");
map.put("buyer_email", transactionId);
orderService.insertPayLog(map);
}else{
reXML.append("<return_code><![CDATA[FAIL]]></return_code>");
reXML.append("<return_msg><![CDATA[订单状态异常]]></return_msg>");
}
}else{
reXML.append("<return_code><![CDATA[FAIL]]></return_code>");
reXML.append("<return_msg><![CDATA[参数[out_trade_no]错误]]></return_msg>");
}
}else{
reXML.append("<return_code><![CDATA[FAIL]]></return_code>");
reXML.append("<return_msg><![CDATA[参数[out_trade_no]为空]]></return_msg>");
}
}else{
<span style="white-space:pre">	</span>logger.info("交易状态异常:"+resultCode);
}
}else{
reXML.append("<return_code><![CDATA[FAIL]]></return_code>");
reXML.append("<return_msg><![CDATA[签名失败]]></return_msg>");
}
}else{
logger.info("微信支付回调失败,失败原因:"+result.get("return_msg").toString());
}
<span style="white-space:pre">	</span>reXML.append("</xml>");
<span style="white-space:pre">	</span>logger.info("返回给微信的XML:\n"+reXML);
<span style="white-space:pre">	</span>response.getWriter().write(new String(reXML.toString().getBytes("UTF-8"),"ISO-8859-1"));
} catch (Exception e) {
<span style="white-space:pre">	</span>logger.info(e.getMessage(), e);
}


支付成功后微信会调用这个方法,这个方法主要用于修改订单状态。当订单状态改变了,js的queryOrderStatus方法检测到订单支付完成了,就会跳转到支付完成页面。就完成了。把js轮训方法也贴上
//查询订单状态
function queryOrderStatus(orderId,intel){
$.post("/xxx/xxxx.jspx",{"orderId":orderId},
function(data){
if(data && data.orderId == orderId){
//查询成功
if(data.code=="ok"){
//订单查询成功
if(data.status==1){
//支付成功,清除轮训并刷新页面
clearInterval(intel);
window.location.href = "/xxxx/xxxx.jspx?orderId="+orderId+"&guid="+guid();
}
}
}
});
}
酱紫微信支付就完成了。


下面说说支付宝支付,因为支付官方提供的有demo 拿过来改改就能直接用了,这个比微信方便多了。直接贴代码

//支付宝支付
//把请求参数打包成数组
Map<String, String> params = new HashMap<String, String>();
params.put("service", AlipayConfig.service);
params.put("partner", AlipayConfig.partner);
params.put("seller_id", AlipayConfig.seller_id);
params.put("_input_charset", AlipayConfig.input_charset);
//params.put("qr_pay_mode", AlipayConfig.qr_pay_mode);
//异步通知支付结果连接
//params.put("notify_url", AlipayConfig.notify_url);
//支付宝处理完请求后,自动跳转到指定页面
params.put("return_url", AlipayConfig.return_url);
//傻逼支付宝“anti_phishing_key”同步回调不返回这个参数,请求的时候不传递
//params.put("anti_phishing_key", AlipaySubmit.query_timestamp());
//支付用户的ip
params.put("exter_invoke_ip", AlipayConfig.exter_invoke_ip);
params.put("out_trade_no", order.getOrderNumber());
params.put("subject", body.toString());
params.put("payment_type", AlipayConfig.payment_type);
params.put("it_b_pay", AlipayConfig.it_b_pay);
params.put("total_fee", sumprice);
params.put("body", body.toString());

//请求支付接口
html = AlipaySubmit.buildRequest(params, "POST","确认");
logger.info("支付宝请求HTML:\n"+html);
//更新订单的支付方式,金额等
order.setPayAmount(Float.parseFloat(sumprice));
order.setPayType(payType);
if(orderService.updatePay(order) > 0){
result.put("code", "ok");
}else{
result.put("code", "err");
}
result.put("msg", html);


我这里用的是同步回调方式,当然也可以用异步和微信是一样的,支付宝是拼接一个自动提交的form表单,然后把这歌表单写到页面就能跳转到支付宝收银台,代码在上面微信生成二维码那里,拿下来

//支付宝
$("#ali_pay").html(data.msg);


这样子表单会自动提交,页面会跳转到支付宝收银台,完成支付后会从支付宝那里跳转 需要一个处理的方法,贴代码

try {
//获取支付宝POST过来反馈信息
Map<String,String> params = new HashMap<String,String>();
Map requestParams = request.getParameterMap();
for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) {
String name = (String) iter.next();
String[] values = (String[]) requestParams.get(name);
String valueStr = "";
for (int i = 0; i < values.length; i++) {
valueStr = (i == values.length - 1) ? valueStr + values[i]
: valueStr + values[i] + ",";
}
//乱码解决,这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
//valueStr = new String(valueStr.getBytes("ISO-8859-1"), "UTF-8");
params.put(name, valueStr);
}

//商户订单号
String out_trade_no = request.getParameter("out_trade_no");

//支付宝交易号
String trade_no = request.getParameter("trade_no");

//交易状态
String trade_status = request.getParameter("trade_status");

//支付价格
String total_fee = request.getParameter("total_fee");

String seller_id = request.getParameter("seller_id");

//买家支付宝账号
String buyer_email = request.getParameter("buyer_email");

//产品描述
String body = request.getParameter("body");

Float total_pay = Float.parseFloat(total_fee);

boolean verify_result = AlipayNotify.verify(params);

if(verify_result){//验证成功
Order o = orderService.findByOrderNum(out_trade_no);
if(o != null){
//请务必判断请求时的total_fee、seller_id与通知时获取的total_fee、seller_id为一致的
if(o.getPayAmount() == total_pay && AlipayConfig.seller_id.equals(seller_id)){
//请求参数和回调参数一致
if(trade_status.equals("TRADE_FINISHED") || trade_status.equals("TRADE_SUCCESS")){
//注意:付款完成后,支付宝系统发送该交易状态通知
//判断该笔订单是否是待付款状态
if(o.getStatus() == 0){
//如果没有做过处理,更改订单状态为确认中状态(1),更新支付完成时间
orderService.updateStatus(o.getId(), 1);
orderService.updatePayCompleteTime(o.getId());

//插入支付日志
Map<String,String> map = new HashMap<String,String>();
map.put("pro_type", "销售");
map.put("pay_type", "支付宝");
map.put("pro_info", body);
map.put("user_name", o.getBuyMember().getUsername());
map.put("trade_no", trade_no);
map.put("pay_amount", o.getPayAmount()+"");
map.put("status", "支付完成");
map.put("buyer_email", buyer_email);
orderService.insertPayLog(map);
//跳转到支付成功页面
return "redirect:/xxx/xxxx.jspx?orderId="+o.getId();
}
}
}
}
}else{//验证失败
response.setContentType("text/html;charset=UTF-8");
response.setCharacterEncoding("UTF-8");
response.getWriter().write("Sign Failure , 验签失败!");
}
} catch (Exception e) {
logger.info(e.getMessage(), e);
}
这个地方要注意 支付宝给的demo 是这样写的

<span style="white-space:pre">	</span>乱码解决,这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
<span style="white-space:pre">	</span>valueStr = new String(valueStr.getBytes("ISO-8859-1"), "UTF-8");


这里千万不要一上来就用“UTF-8” 不然会乱码 然后就会出现签名错误的问题。至此微信和支付宝支付都做成功了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息