扫码支付
本文附有代码,在下方,如果不熟悉场景的可以看看下面的场景介绍
场景介绍
官网介绍地址:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1
用户扫描商户展示在各种场景的二维码进行支付。
步骤1:商户根据微信支付的规则,为不同商品生成不同的二维码(如图6.1),展示在各种场景,用于用户扫描购买。
步骤2:用户使用微信“扫一扫”(如图6.2)扫描二维码后,获取商品支付信息,引导用户完成支付(如图6.3)。
图6.1 支付二维码 |
图6.2 打开微信扫一扫二维码 |
图6.3 确认支付页面 |
步骤(3):用户确认支付,输入支付密码(如图6.4)。
步骤(4):支付完成后会提示用户支付成功(如图6.5),商户后台得到支付成功的通知,然后进行发货处理。
图6.4 用户确认支付,输入密码 |
图6.5 支付成功提示 |
模式二与模式一相比,流程更为简单,不依赖设置的回调支付URL。商户后台系统先调用微信支付的统一下单接口,微信后台系统返回链接参数code_url,商户后台系统将code_url值生成二维码图片,用户使用微信客户端扫码后发起支付。注意:code_url有效期为2小时,过期后扫码不能再发起支付。
业务流程时序图
图6.9 原生支付模式二时序图
业务流程说明:
(1)商户后台系统根据用户选购的商品生成订单。
(2)用户确认支付后调用微信支付【统一下单API】生成预支付交易;
(3)微信支付系统收到请求后生成预支付交易单,并返回交易会话的二维码链接code_url。
(4)商户后台系统根据返回的code_url生成二维码。
(5)用户打开微信“扫一扫”扫描二维码,微信客户端将扫码内容发送到微信支付系统。
(6)微信支付系统收到客户端请求,验证链接有效性后发起用户支付,要求用户授权。
(7)用户在微信客户端输入密码,确认支付后,微信客户端提交授权。
(8)微信支付系统根据用户授权完成支付交易。
(9)微信支付系统完成支付交易后给微信客户端返回交易结果,并将交易结果通过短信、微信消息提示用户。微信客户端展示支付交易结果页面。
(10)微信支付系统通过发送异步消息通知商户后台系统支付结果。商户后台系统需回复接收情况,通知微信后台系统不再发送该单的支付通知。
(11)未收到支付通知的情况,商户后台系统调用【查询订单API】。
(12)商户确认订单已支付后给用户发货。
生成二维码规则
对应链接格式:weixin://wxpay/bizpayurl?sr=XXXXX。请商户调用第三方库将code_url生成二维码图片。该模式链接较短,生成的二维码打印到结账小票上的识别率较高。
例如,将weixin://wxpay/s/An4baqw生成二维码见图6.10。
图6.10 原生支付“模式二”二维码示例
下面上代码:
这里特要注意的就是key,这个key不是公众号下面的key,而是商户下的key,这个key是自己生成的32位字符串,
调用的是微信统一下单接口:
getWxPayQRCode() 返回的就是要扫的二维码中的值;只要把这个值塞到二维中即可!
/** * 微信扫码支付 模式二 * @return返回的就是二维中的值 * @throws Exception */ public String getWxPayQRCode() throws Exception{ //商户key String key = "mlqho2dwhxxxxxxxxxxxv81m1r6i28"; SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss"); Map<String,String> map = new HashMap<String, String>(); //公众账号ID map.put("appid","wssssssxxxxxxxxxe0e5b"); //商户号 map.put("mch_id","1xxxxxxxxx2"); //随机数 map.put("nonce_str", RandomStringGenerator.getRandomStringByLength(32)); //商品描述 map.put("body","香辣鸡腿堡"); //订单号 map.put("out_trade_no",sdf.format(new Date())); //总金额单位分 map.put("total_fee","1"); InetAddress ia = InetAddress.getLocalHost(); //ip map.put("spbill_create_ip",ia.getHostAddress()); //通知地址 map.put("notify_url","www.niudao.com"); //交易类型 map.put("trade_type","NATIVE"); //商品ID map.put("product_id","10000"); //签名 map.put("sign", QRSign.getSign(map, key)); //请求地址及请求数据 String url = "https://api.mch.weixin.qq.com/pay/unifiedorder"; String data = MapToXmlUtil.mapToXml(map); //发送请求 ClientRequest request = new ClientRequest(url); request.body("text/xml;charset=utf-8",data); ClientResponse response = request.post(String.class); //xml字符串转化成json对象 String result = XMLAndJsonUtil.xmlChangeJson(response.getEntity().toString()); result = result.replaceAll("\r\n",""); JSONObject jsonObject = JSONObject.fromObject(result); //二维码内容 String qrCode = ""; if(jsonObject.get("return_code").toString().equals("SUCCESS")){ qrCode = jsonObject.get("code_url").toString(); } return qrCode; }
工具类
RandomStringGenerator 传入一个n,生成n位随机字符串
import java.util.Random; /** * User: rizenguo * Date: 2014/10/29 * Time: 14:18 */ public class RandomStringGenerator { /** * 获取一定长度的随机字符串 * @param length 指定字符串长度 * @return 一定长度的字符串 */ public static String getRandomStringByLength(int length) { String base = "abcdefghijklmnopqrstuvwxyz0123456789"; Random random = new Random(); StringBuffer sb = new StringBuffer(); for (int i = 0; i < length; i++) { int number = random.nextInt(base.length()); sb.append(base.charAt(number)); } return sb.toString(); } }
QRSign 传入的数据生成签名,
import java.security.MessageDigest; import java.util.Arrays; import java.util.Map; /** * Created by Administrator on 16-12-1. */ public class QRSign { /** * 微信支付签名算法sign * @param map 请求微支付body * @param key 商户key (不是公众号key) * @return */ public static String getSign(Map<String,String> map,String key) { StringBuffer sb = new StringBuffer(); String[] keyArr = (String[]) map.keySet().toArray(new String[map.keySet().size()]);//获取map中的key转为array Arrays.sort(keyArr);//对array排序 for (int i = 0, size = keyArr.length; i < size; ++i) { if ("sign".equals(keyArr[i])) { continue; } sb.append(keyArr[i] + "=" + map.get(keyArr[i]) + "&"); } sb.append("key=" + key); String sign = string2MD5(sb.toString()); return sign; } /*** * MD5加密 生成32位md5码 */ public static String string2MD5(String str){ if (str == null || str.length() == 0) { return null; } char hexDigits[] = { ‘0‘, ‘1‘, ‘2‘, ‘3‘, ‘4‘, ‘5‘, ‘6‘, ‘7‘, ‘8‘, ‘9‘, ‘a‘, ‘b‘, ‘c‘, ‘d‘, ‘e‘, ‘f‘ }; try { MessageDigest mdTemp = MessageDigest.getInstance("MD5"); mdTemp.update(str.getBytes("UTF-8")); byte[] md = mdTemp.digest(); int j = md.length; char buf[] = new char[j * 2]; int k = 0; for (int i = 0; i < j; i++) { byte byte0 = md[i]; buf[k++] = hexDigits[byte0 >>> 4 & 0xf]; buf[k++] = hexDigits[byte0 & 0xf]; } return new String(buf).toUpperCase(); } catch (Exception e) { return null; } } }
MapToXmlUtil作用:
由于微信支付接口传入的参数是xml格式的,因此此类是将map转化为xml格式
import java.util.Map; /** * Created by Administrator on 16-12-1. */ public class MapToXmlUtil { /** * 将map 转化成 xml * @param map * @return */ public static String mapToXml(Map<String,String> map)throws Exception{ StringBuilder sb = new StringBuilder(); sb.append("<xml>"); for (Map.Entry<String, String> entry : map.entrySet()) { sb.append("<" + entry.getKey() + ">" + entry.getValue().toString() + "</" + entry.getKey() + ">"); } sb.append("</xml>"); return sb.toString(); } }
最终返回的结果也是xml格式的,为了方便取值,本人转成json格式;
import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.DocumentHelper; import org.dom4j.Element; import org.dom4j.io.SAXReader; import java.io.File; import java.util.ArrayList; import java.util.Iterator; import java.util.List; /** * Created by Administrator on 16-12-1. */ public class XMLAndJsonUtil { //用于判断是否有子节点,若有就将子节点也进行拼接,若无则返回"" public static String checkChildEle(Element element) throws DocumentException { String json=""; List<Element> list = new ArrayList<Element>(); list=element.elements(); if (list.size()>0) { for (Element ele : list) { json+= "‘" + ele.getName() + "‘" + ":" + "‘" + ele.getText() + "‘" + "," + "\r\n" + checkChildEle(ele); } } return json; } //这个方法是将xml字符串转成Json public static String xmlChangeJson(String XML) throws DocumentException{ Document document= DocumentHelper.parseText(XML); Element root=document.getRootElement(); Iterator it=root.elementIterator(); String json="{"; while (it.hasNext()) { Element element =(Element)it.next(); String j=checkChildEle(element); if (j=="") { json+= "‘" + element.getName() + "‘" + ":" + "‘" + element.getText() + "‘" + ","+"\r\n"; }else { json+=j; } } json+="}"; return json; } //这个方法是将xml文件转成Json public static String xmlChangeJson(File XML) throws DocumentException{ SAXReader reader=new SAXReader(); Document document=reader.read(XML); Element root=document.getRootElement(); Iterator it=root.elementIterator(); String json="{"; while (it.hasNext()) { Element element =(Element)it.next(); String j=checkChildEle(element); if (j=="") { json+=element.getName()+":"+element.getText()+","+"\r\n"; }else { json+=j; } } json+="}"; return json; } }
如果报签名错误可以到下面这个网址去验证sign生成的结果是否一致
https://pay.weixin.qq.com/wiki/tools/signverify/