天天看点

JAVA实现的微信扫描二维码支付

支付项目采用springMvc+Dubbo架构实现,只对外提供接口。

话说,为什么微信支付比支付宝来的晚了那么一点,一句话,那一阵挺忙的,然后就没有时间整理,最近做完支付宝支付,顺便也把微信支付的也整理一下。

这里再吐槽一下,微信支付的DEMO基本为零,很多代码都是从网上查找的(也可能我么有仔细找API)。

扫码支付,目前来说个人是不可以申请的,包括现在支付宝的即时到帐个人相关业务也取消了。所以这里必须有一个微信支付商户平台,具体怎么申请的,我也不清楚,只是拿来用的。

商户平台是要配合绑定微信公众账号使用的,具体操作申请下来已经绑定了,这里你也只管用就是了。

场景介绍

用户扫描商户展示在各种场景的二维码进行支付。

步骤1:商户根据微信支付的规则,为不同商品生成不同的二维码(如图6.1),展示在各种场景,用于用户扫描购买。

步骤2:用户使用微信“扫一扫”(如图6.2)扫描二维码后,获取商品支付信息,引导用户完成支付(如图6.3)。

支付二维码

图6.1 支付二维码

打开微信扫一扫二维码

图6.2 打开微信扫一扫二维码

确认支付页面

图6.3 确认支付页面

步骤(3):用户确认支付,输入支付密码(如图6.4)。

步骤(4):支付完成后会提示用户支付成功(如图6.5),商户后台得到支付成功的通知,然后进行发货处理。

用户确认支付,输入密码

图6.4 用户确认支付,输入密码

支付成功提示

图6.5 支付成功提示

ConfigUtil参数配置:

<code>import java.util.Map;</code>

<code>import java.util.ResourceBundle;</code>

<code>import java.util.SortedMap;</code>

<code>import java.util.TreeMap;</code>

<code></code>

<code>/**</code>

<code>* 相关配置参数</code>

<code>* 创建者 张志朋</code>

<code>* 创建时间 2016年9月28日</code>

<code>*</code>

<code>*/</code>

<code>public class ConfigUtil {</code>

<code>* 服务号相关信息</code>

<code>public final static String APP_ID = "2016";//服务号的应用ID</code>

<code>public final static String APP_SECRET = "2016";//服务号的应用密钥</code>

<code>public final static String TOKEN = "weixinCourse";//服务号的配置token</code>

<code>public final static String MCH_ID = "2016";//商户号</code>

<code>public final static String API_KEY = "2016";//API密钥</code>

<code>public final static String SIGN_TYPE = "MD5";//签名加密方式</code>

<code>public final static String CERT_PATH = "apiclient_cert.p12";//微信支付证书存放路径地址</code>

<code>static ResourceBundle resource = ResourceBundle.getBundle("config");</code>

<code>//微信支付统一接口的回调action</code>

<code>public final static String NOTIFY_URL = resource.getString("WEIXIN_NOTIFY_URL");</code>

<code>* 微信基础接口地址</code>

<code>//获取token接口(GET)</code>

<code>public final static String TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&amp;appid=APPID&amp;secret=APPSECRET";</code>

<code>//oauth2授权接口(GET)</code>

<code>public final static String OAUTH2_URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&amp;secret=SECRET&amp;code=CODE&amp;grant_type=authorization_code";</code>

<code>//刷新access_token接口(GET)</code>

<code>public final static String REFRESH_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=APPID&amp;grant_type=refresh_token&amp;refresh_token=REFRESH_TOKEN";</code>

<code>// 菜单创建接口(POST)</code>

<code>public final static String MENU_CREATE_URL = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN";</code>

<code>// 菜单查询(GET)</code>

<code>public final static String MENU_GET_URL = "https://api.weixin.qq.com/cgi-bin/menu/get?access_token=ACCESS_TOKEN";</code>

<code>// 菜单删除(GET)</code>

<code>public final static String MENU_DELETE_URL = "https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=ACCESS_TOKEN";</code>

<code>* 微信支付接口地址</code>

<code>//微信支付统一接口(POST)</code>

<code>public final static String UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";</code>

<code>//微信退款接口(POST)</code>

<code>public final static String REFUND_URL = "https://api.mch.weixin.qq.com/secapi/pay/refund";</code>

<code>//订单查询接口(POST)</code>

<code>public final static String CHECK_ORDER_URL = "https://api.mch.weixin.qq.com/pay/orderquery";</code>

<code>//关闭订单接口(POST)</code>

<code>public final static String CLOSE_ORDER_URL = "https://api.mch.weixin.qq.com/pay/closeorder";</code>

<code>//退款查询接口(POST)</code>

<code>public final static String CHECK_REFUND_URL = "https://api.mch.weixin.qq.com/pay/refundquery";</code>

<code>//对账单接口(POST)</code>

<code>public final static String DOWNLOAD_BILL_URL = "https://api.mch.weixin.qq.com/pay/downloadbill";</code>

<code>//短链接转换接口(POST)</code>

<code>public final static String SHORT_URL = "https://api.mch.weixin.qq.com/tools/shorturl";</code>

<code>//接口调用上报接口(POST)</code>

<code>public final static String REPORT_URL = "https://api.mch.weixin.qq.com/payitil/report";</code>

<code>public static void commonParams(SortedMap&lt;Object, Object&gt; packageParams){</code>

<code>// 账号信息</code>

<code>String appid = ConfigUtil.APP_ID; // appid</code>

<code>String mch_id = ConfigUtil.MCH_ID; // 商业号</code>

<code>// 生成随机字符串</code>

<code>String currTime = PayCommonUtil.getCurrTime();</code>

<code>String strTime = currTime.substring(8, currTime.length());</code>

<code>String strRandom = PayCommonUtil.buildRandom(4) + "";</code>

<code>String nonce_str = strTime + strRandom;</code>

<code>packageParams.put("appid", appid);// 公众账号ID</code>

<code>packageParams.put("mch_id", mch_id);// 商户号</code>

<code>packageParams.put("nonce_str", nonce_str);// 随机字符串</code>

<code>}</code>

<code>* 该接口主要用于扫码原生支付模式一中的二维码链接转成短链接(weixin://wxpay/s/XXXXXX),减小二维码数据量,提升扫描速度和精确度。</code>

<code>* @Author 张志朋</code>

<code>* @param urlCode void</code>

<code>* @Date 2016年10月26日</code>

<code>* 更新日志</code>

<code>* 2016年10月26日 张志朋 首次创建</code>

<code>@SuppressWarnings("rawtypes")</code>

<code>public static void shorturl(String urlCode){</code>

<code>try {</code>

<code>String key = ConfigUtil.API_KEY; // key</code>

<code>SortedMap&lt;Object, Object&gt; packageParams = new TreeMap&lt;Object, Object&gt;();</code>

<code>ConfigUtil.commonParams(packageParams);</code>

<code>packageParams.put("long_url", urlCode);// URL链接</code>

<code>String sign = PayCommonUtil.createSign("UTF-8", packageParams, key);</code>

<code>packageParams.put("sign", sign);// 签名</code>

<code>String requestXML = PayCommonUtil.getRequestXml(packageParams);</code>

<code>String resXml = HttpUtil.postData(ConfigUtil.SHORT_URL, requestXML);</code>

<code>Map map = XMLUtil.doXMLParse(resXml);</code>

<code>String returnCode = (String) map.get("return_code");</code>

<code>if("SUCCESS".equals(returnCode)){</code>

<code>String resultCode = (String) map.get("return_code");</code>

<code>if("SUCCESS".equals(resultCode)){</code>

<code>urlCode = (String) map.get("short_url");</code>

<code>} catch (Exception e) {</code>

<code>e.printStackTrace();</code>

参数必填项 APP_ID 和APP_SECRET 是从微信公众号里面获取的,而MCH_ID和API_KEY是从商户平台获取的。CERT_PATH 证书可选,但是如果做退款接口必须要使用证书。NOTIFY_URL 为回调地址,自定义路径,但是一定要微信平台可以调用到你的url。

API文档地址

文档有详细的参数说明,具体生成需要xml解析,这里就不放了,好多的说,有需要的可以留言。

支付回调:

<code>* 二维码支付</code>

<code>* 创建时间 2016年10月31日</code>

<code>@Controller</code>

<code>@RequestMapping(value = "weixin")</code>

<code>public class WeixinPayController {</code>

<code>@Autowired</code>

<code>private IWeixinPayService weixinpayBack;</code>

<code>* 微信支付回调</code>

<code>* @param request</code>

<code>* @param response</code>

<code>* @throws Exception void</code>

<code>* @Date 2016年9月28日</code>

<code>* 2016年9月28日 张志朋 首次创建</code>

<code>@SuppressWarnings({ "unchecked", "rawtypes" })</code>

<code>@RequestMapping(value = "pay")</code>

<code>public void weixin_notify(HttpServletRequest request, HttpServletResponse response) throws Exception {</code>

<code>LogUtil.info("支付成功回调");</code>

<code>// 读取参数</code>

<code>InputStream inputStream = request.getInputStream();</code>

<code>StringBuffer sb = new StringBuffer();</code>

<code>String s;</code>

<code>BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));</code>

<code>while ((s = in.readLine()) != null) {</code>

<code>sb.append(s);</code>

<code>in.close();</code>

<code>inputStream.close();</code>

<code>// 解析xml成map</code>

<code>Map&lt;String, String&gt; m = new HashMap&lt;String, String&gt;();</code>

<code>m = XMLUtil.doXMLParse(sb.toString());</code>

<code>// 过滤空 设置 TreeMap</code>

<code>Iterator it = m.keySet().iterator();</code>

<code>while (it.hasNext()) {</code>

<code>String parameter = (String) it.next();</code>

<code>String parameterValue = m.get(parameter);</code>

<code>String v = "";</code>

<code>if (null != parameterValue) {</code>

<code>v = parameterValue.trim();</code>

<code>packageParams.put(parameter, v);</code>

<code>// 判断签名是否正确</code>

<code>if (PayCommonUtil.isTenpaySign("UTF-8", packageParams, key)) {</code>

<code>// ------------------------------</code>

<code>// 处理业务开始</code>

<code>String resXml = "";</code>

<code>if ("SUCCESS".equals((String) packageParams.get("result_code"))) {</code>

<code>// 这里是支付成功</code>

<code>String orderNo = (String) packageParams.get("out_trade_no");</code>

<code>String attach = (String) packageParams.get("attach");</code>

<code>//回调K12</code>

<code>LogUtil.info(attach+"(订单号:"+orderNo+"付款成功)");</code>

<code>// 通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.</code>

<code>resXml = "&lt;xml&gt;" + "&lt;return_code&gt;&lt;![CDATA[SUCCESS]]&gt;&lt;/return_code&gt;" + "&lt;return_msg&gt;&lt;![CDATA[OK]]&gt;&lt;/return_msg&gt;" + "&lt;/xml&gt; ";</code>

<code>weixinpayBack.updateAccOrder(orderNo);</code>

<code>} else {</code>

<code>LogUtil.info("支付失败,错误信息:" + packageParams.get("err_code"));</code>

<code>resXml = "&lt;xml&gt;" + "&lt;return_code&gt;&lt;![CDATA[FAIL]]&gt;&lt;/return_code&gt;" + "&lt;return_msg&gt;&lt;![CDATA[报文为空]]&gt;&lt;/return_msg&gt;"+ "&lt;/xml&gt; ";</code>

<code>// 处理业务完毕</code>

<code>BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());</code>

<code>out.write(resXml.getBytes());</code>

<code>out.flush();</code>

<code>out.close();</code>

<code>LogUtil.info("通知签名验证失败");</code>

大体就这个样子,后续的可能就是安全优化了。涉及到钱可不是小问题。