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

微信提供两种扫码支付模式,模式一是先生成一个微信规定格式的二维码供用户扫描,引导完成支付,需要在服务号上配置回调地址。模式二通过后台向微信提交支付申请,微信提供一个连接,将连接生成二维码后供用户扫描完成支付。
官方解释:
【模式一】:商户后台系统根据微信支付规则链接生成二维码,链接中带固定参数productid(可定义为产品标识或订单号)。用户扫码后,微信支付系统将productid和用户唯一标识(openid)回调商户后台系统(需要设置支付回调URL),商户后台系统根据productid生成支付交易,最后微信支付系统发起用户支付流程。
【模式二】:商户后台系统调用微信支付【统一下单API】生成预付交易,将接口返回的链接生成二维码,用户扫码后输入密码完成支付交易。注意:该模式的预付单有效期为2小时,过期后无法支付。
本文采用模式二。(官方文档:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_5)


业务流程说明:

(1)商户后台系统根据用户选购的商品生成订单。

(2)用户确认支付后调用微信支付【统一下单API】生成预支付交易;

(3)微信支付系统收到请求后生成预支付交易单,并返回交易会话的二维码链接code_url。

(4)商户后台系统根据返回的code_url生成二维码。

(5)用户打开微信“扫一扫”扫描二维码,微信客户端将扫码内容发送到微信支付系统。

(6)微信支付系统收到客户端请求,验证链接有效性后发起用户支付,要求用户授权。

(7)用户在微信客户端输入密码,确认支付后,微信客户端提交授权。

(8)微信支付系统根据用户授权完成支付交易。

(9)微信支付系统完成支付交易后给微信客户端返回交易结果,并将交易结果通过短信、微信消息提示用户。微信客户端展示支付交易结果页面。

(10)微信支付系统通过发送异步消息通知商户后台系统支付结果。商户后台系统需回复接收情况,通知微信后台系统不再发送该单的支付通知。

(11)未收到支付通知的情况,商户后台系统调用【查询订单API】。

(12)商户确认订单已支付后给用户发货。
具体代码:
action中代码:

/*** 微信支付的action* @author * @since 20180504*/
@Namespace("/QRpay")
public class WeChatPayAction extends BasicAction<DoctorAccount>{private static final long serialVersionUID = 1L;// 微信的参数private WeixinConfigUtils config = new WeixinConfigUtils();@Resourceprivate IBasicsConfigService basicsConfigService;private Unifiedorder unifiedorder = new Unifiedorder();@Action("generateOrder")public String generateOrder(){String candy = Struts2Utils.getRequest().getParameter("candy");if (StringUtils.isNotBlank(candy)) {Integer score = Integer .valueOf(candy);String url = null;String out_trade_no = UuIdUtils.getUUID();//从redis中获取固定的连接地址String domainName = basicsConfigService.getBasicsConfig();// 参数组String appid = config.appid_doctor;String mch_id = config.mch_id_doctor;String nonce_str = RandCharsUtils.getRandomString(16);String body = "";body = "医师购买" + score + "康币,支付" + score + "元";String spbill_create_ip = "127.0.0.1";int total_fee = score;// 单位是分,现在按照ios传递过来的参数进行String notify_url = domainName+"/weixinPay/notifyDoctorUrl.do";String trade_type = "NATIVE";unifiedorder = WXSignUtils.getQRUnifiedorder(appid, mch_id, body, nonce_str, out_trade_no, total_fee, notify_url, trade_type, spbill_create_ip, "2");try {Map<String, String> map = HttpXmlUtils.getQRMap(unifiedorder);for (String value : map.keySet()) {if (map.get("return_code").equals("SUCCESS")) {url =  map.get("code_url");}System.out.println(value + map.get(value));}} catch (Exception e) {// TODO: handle exception}}return null;}
}

WeixinConfigUtils:

/*** 微信的配置参数* @author * @date 2017/08/10*/
@SuppressWarnings("unused")
public class WeixinConfigUtils {private static final Log log = LogFactory.getLog(WeixinConfigUtils.class);public static String appid;public static String mch_id;public static String mch_id_doctor;public static String appid_doctor;static {try{InputStream is = WeixinConfigUtils.class.getResourceAsStream("/weixin.properties");Properties properties = new Properties();properties.load(is);appid = properties.getProperty("weixin.appid");mch_id = properties.getProperty("weixin.mch_id");appid_doctor=properties.getProperty("weixin.doctor.appid");mch_id_doctor = properties.getProperty("weixin.doctor.mch_id");}catch(Exception ex){log.debug("加载配置文件:"+ex.getMessage());}}
}

Unifiedorder用于封装微信相关的请求字段:包括退款,扫码支付、app支付、h5支付、转账到个人

/*** 统一下单提交为微信的参数* @author * @date 2017年08月11日*/
public class Unifiedorder implements Serializable{private static final long serialVersionUID = 1L;//微信支付表idprivate Integer weixinId;//微信分配的公众账号ID(企业号corpid即为此appId)private String appid;//商户idprivate String mch_id;//终端设备号(门店号或收银设备ID),注意:PC网页或公众号内支付请传"WEB"private String device_info;//随机字符串:数字+大写字母的组合,32位private String nonce_str;//签名private String sign;//商品或支付单简要描述private String body;//商品名称明细列表private String detail;//附加参数(例如:用于区别本商户不同的分店)private String attach;//商户系统内部的订单号private String out_trade_no;//货币类型:符合ISO 4217标准的三位字母代码,默认人民币:CNYprivate String fee_type;//总金额private int total_fee;//APP和网页支付提交[用户端ip],Native支付填调用微信支付API的机器IP。private String spbill_create_ip;//订单生成时间,格式为yyyyMMddHHmmss,private String time_start;//订单失效时间,格式为yyyyMMddHHmmss,最短失效时间间隔必须大于5分钟[支付宝是30分钟,同样30分钟]private String time_expire;//商品标记,代金券或立减优惠功能的参数private String goods_tag;//接收微信支付异步通知回调地址private String notify_url;//交易类型:JSAPI,NATIVE,APP h5为 MWEBprivate String trade_type;//trade_type=NATIVE,此参数必传。此id为二维码中包含的商品ID,商户自行定义。private String product_id;//no_credit--指定不能使用信用卡支付private String limit_pay;//trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识private String openid;//商户内部自己的退款单号private String out_refund_no;//退款总金额单位为分private int refund_fee;//操作员的id默认为mch_idprivate String op_user_id;//微信官方提供的订单号private String prepayid;//记录所对应的memberprivate Member member;//返回给微信的状态码(用于支付回调时)public String return_code;//微信h5支付时候的场景信息官方的信息模板 {"h5_info"://h5支付固定传"h5_info" //{"type":"",//场景类型 "wap_url":"",//WAP网站URL地址"wap_name": ""//WAP 网站名}}public String scene_info;public String getScene_info() {return scene_info;}public void setScene_info(String scene_info) {this.scene_info = scene_info;}public String getReturn_code() {return return_code;}public void setReturn_code(String return_code) {this.return_code = return_code;}public String getAppid() {return appid;}public String getMch_id() {return mch_id;}public String getDevice_info() {return device_info;}public String getNonce_str() {return nonce_str;}public String getSign() {return sign;}public String getBody() {return body;}public String getDetail() {return detail;}public String getAttach() {return attach;}public String getOut_trade_no() {return out_trade_no;}public String getFee_type() {return fee_type;}public int getTotal_fee() {return total_fee;}public String getSpbill_create_ip() {return spbill_create_ip;}public String getTime_start() {return time_start;}public String getTime_expire() {return time_expire;}public String getGoods_tag() {return goods_tag;}public String getNotify_url() {return notify_url;}public String getTrade_type() {return trade_type;}public String getProduct_id() {return product_id;}public String getLimit_pay() {return limit_pay;}public String getOpenid() {return openid;}public void setAppid(String appid) {this.appid = appid;}public void setMch_id(String mch_id) {this.mch_id = mch_id;}public void setDevice_info(String device_info) {this.device_info = device_info;}public void setNonce_str(String nonce_str) {this.nonce_str = nonce_str;}public void setSign(String sign) {this.sign = sign;}public void setBody(String body) {this.body = body;}public void setDetail(String detail) {this.detail = detail;}public void setAttach(String attach) {this.attach = attach;}public void setOut_trade_no(String out_trade_no) {this.out_trade_no = out_trade_no;}public void setFee_type(String fee_type) {this.fee_type = fee_type;}public void setTotal_fee(int total_fee) {this.total_fee = total_fee;}public void setSpbill_create_ip(String spbill_create_ip) {this.spbill_create_ip = spbill_create_ip;}public void setTime_start(String time_start) {this.time_start = time_start;}public void setTime_expire(String time_expire) {this.time_expire = time_expire;}public void setGoods_tag(String goods_tag) {this.goods_tag = goods_tag;}public void setNotify_url(String notify_url) {this.notify_url = notify_url;}public void setTrade_type(String trade_type) {this.trade_type = trade_type;}public void setProduct_id(String product_id) {this.product_id = product_id;}public void setLimit_pay(String limit_pay) {this.limit_pay = limit_pay;}public void setOpenid(String openid) {this.openid = openid;}public String getOut_refund_no() {return out_refund_no;}public void setOut_refund_no(String out_refund_no) {this.out_refund_no = out_refund_no;}public int getRefund_fee() {return refund_fee;}public void setRefund_fee(int refund_fee) {this.refund_fee = refund_fee;}public Integer getWeixinId() {return weixinId;}public void setWeixinId(Integer weixinId) {this.weixinId = weixinId;}public Member getMember() {return member;}public void setMember(Member member) {this.member = member;}public String getPrepayid() {return prepayid;}public void setPrepayid(String prepayid) {this.prepayid = prepayid;}public String getOp_user_id() {return op_user_id;}public void setOp_user_id(String op_user_id) {this.op_user_id = op_user_id;}}

UuIdUtils代码:

public class UuIdUtils {public static String getUUID() {return UUID.randomUUID().toString().replace("-", "");}
}

RandCharsUtils代码:

/*** nonce_str随即字符串
* @author * @date 2017/08/10*/
public class RandCharsUtils {private static SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");public static String getRandomString(int length) { //length表示生成字符串的长度String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";   Random random = new Random();   StringBuffer sb = new StringBuffer();int number = 0;for (int i = 0; i < length; i++) {   number = random.nextInt(base.length());   sb.append(base.charAt(number));   }   return sb.toString();   }   /** 订单开始交易的时间*/public static String timeStart(){return df.format(new Date());}/** 订单开始交易的时间*/public static String timeExpire(){Calendar now=Calendar.getInstance();now.add(Calendar.MINUTE,30);return df.format(now.getTimeInMillis());}}

WXSignUtils代码

/*** 微信支付签名* @author * @date 2017/08/10*/
public class WXSignUtils {//微信统一签名的对象 private static Unifiedorder unifiedorder = new Unifiedorder();//封装参数的方法private static SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();/*** 微信支付签名算法sign* @param characterEncoding* @param parameters* @return*/@SuppressWarnings("rawtypes")public static String createSign(String characterEncoding,SortedMap<Object,Object> parameters,String type){StringBuffer sb = new StringBuffer();Set es = parameters.entrySet();//所有参与传参的参数按照accsii排序(升序)Iterator it = es.iterator();while(it.hasNext()) {Map.Entry entry = (Map.Entry)it.next();String k = (String)entry.getKey();Object v = entry.getValue();if(null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {sb.append(k + "=" + v + "&");}}if (type.equals("1")) {sb.append("key=" + weixinConstant.KEY);}else if (type.equals("2")) {sb.append("key=" + weixinConstant.DOC_KEY);}String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();return sign;}/*** 微信扫码支付时返回封装好的参数* @author yangfuren* @param appid  应用id* @param mch_id 商户号* @param body 内容* @param nonce_str 随机串* @param out_trade_no  微信商户号* @param total_fee 总价格* @param notify_url 回调url * @param trade_type 支付方式* @param spbill_create_ip 请求接口id* @param scence_info h5支付相关信息* @param type 1用户  2 医师* @return*/public static Unifiedorder getQRUnifiedorder(String appid,String mch_id,String body,String nonce_str,String out_trade_no,Integer total_fee,String notify_url,String trade_type,String spbill_create_ip,String type){parameters.put("appid", appid);parameters.put("mch_id", mch_id);parameters.put("body", body);parameters.put("nonce_str", nonce_str);parameters.put("out_trade_no", out_trade_no);parameters.put("total_fee", total_fee);parameters.put("notify_url", notify_url);parameters.put("trade_type", trade_type);parameters.put("spbill_create_ip", spbill_create_ip);String sign = createSign("UTF-8", parameters,type);// 微信统一下单unifiedorder.setAppid(appid);unifiedorder.setMch_id(mch_id);unifiedorder.setNonce_str(nonce_str);unifiedorder.setSign(sign);unifiedorder.setBody(body);unifiedorder.setOut_trade_no(out_trade_no);unifiedorder.setTotal_fee(total_fee);unifiedorder.setSpbill_create_ip(spbill_create_ip);unifiedorder.setNotify_url(notify_url);unifiedorder.setTrade_type(trade_type);return unifiedorder;}
}

MD5:

public class MD5Util {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()));elseresultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname)));} catch (Exception exception) {}return resultString;}private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5","6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };}

请求参数并解析返回xml:

/*** 构造xml参数* @param xml* @return*/public static String QRxmlInfo(Unifiedorder unifiedorder){if(unifiedorder!=null){StringBuffer bf = new StringBuffer();bf.append("<xml>");bf.append("<appid><![CDATA[");bf.append(unifiedorder.getAppid());bf.append("]]></appid>");bf.append("<mch_id><![CDATA[");bf.append(unifiedorder.getMch_id());bf.append("]]></mch_id>");bf.append("<nonce_str><![CDATA[");bf.append(unifiedorder.getNonce_str());bf.append("]]></nonce_str>");bf.append("<sign><![CDATA[");bf.append(unifiedorder.getSign());bf.append("]]></sign>");bf.append("<body><![CDATA[");bf.append(unifiedorder.getBody());bf.append("]]></body>");bf.append("<out_trade_no><![CDATA[");bf.append(unifiedorder.getOut_trade_no());bf.append("]]></out_trade_no>");bf.append("<total_fee><![CDATA[");bf.append(unifiedorder.getTotal_fee());bf.append("]]></total_fee>");bf.append("<spbill_create_ip><![CDATA[");bf.append(unifiedorder.getSpbill_create_ip());bf.append("]]></spbill_create_ip>");bf.append("<notify_url><![CDATA[");bf.append(unifiedorder.getNotify_url());bf.append("]]></notify_url>");bf.append("<trade_type><![CDATA[");bf.append(unifiedorder.getTrade_type());bf.append("]]></trade_type>");bf.append("</xml>");return bf.toString();}return "";}/*** post请求并得到返回结果* @param requestUrl* @param requestMethod* @param output* @return*/public static String httpsRequest(String requestUrl, String requestMethod, String output) {try{URL url = new URL(requestUrl);HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();connection.setDoOutput(true);connection.setDoInput(true);connection.setUseCaches(false);connection.setRequestMethod(requestMethod);if (null != output) {OutputStream outputStream = connection.getOutputStream();outputStream.write(output.getBytes("UTF-8"));outputStream.close();}// 从输入流读取返回内容InputStream inputStream = connection.getInputStream();InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");BufferedReader bufferedReader = new BufferedReader(inputStreamReader);String str = null;StringBuffer buffer = new StringBuffer();while ((str = bufferedReader.readLine()) != null) {buffer.append(str);}bufferedReader.close();inputStreamReader.close();inputStream.close();inputStream = null;connection.disconnect();return buffer.toString();}catch(Exception ex){ex.printStackTrace();}return "";}/*** h5支付时 解析返回的值并返回prepareid* @throws IOException * @throws JDOMException */public static Map<String, String> getQRMap(Unifiedorder unifiedorder) throws JDOMException, IOException{String xmlInfo = QRxmlInfo(unifiedorder);String wxUrl = weixinConstant.URL;String method = "POST";String weixinPost = HttpXmlUtils.httpsRequest(wxUrl, method, xmlInfo).toString();ParseXMLUtils.jdomParseXml(weixinPost);StringReader read = new StringReader(weixinPost);// 创建新的输入源SAX 解析器将使用 InputSource 对象来确定如何读取 XML 输入InputSource source = new InputSource(read);// 创建一个新的SAXBuilderSAXBuilder sb = new SAXBuilder();// 通过输入源构造一个Documentorg.jdom.Document doc;doc = (org.jdom.Document) sb.build(source);org.jdom.Element root = doc.getRootElement();// 指向根节点List<org.jdom.Element> list = root.getChildren();Map<String, String> msg = new HashMap<String, String>();if(list!=null&&list.size()>0){for (org.jdom.Element element : list) {msg.put(element.getName(), element.getText());}}return msg;}

微信扫码支付(模式二)相关推荐

  1. 微信扫码支付模式二【无法回调】解决方案

    微信扫码支付模式二[无法回调]解决方案 参考文章: (1)微信扫码支付模式二[无法回调]解决方案 (2)https://www.cnblogs.com/lhat/p/5611242.html 备忘一下 ...

  2. JAVA微信扫码支付模式二功能实现完整例子

    概述 本例子实现微信扫码支付模式二的支付功能,应用场景是,web网站微信扫码支付.实现从点击付费按钮.到弹出二维码.到用户用手机微信扫码支付.到手机上用户付费成功.web网页再自动调整到支付成功后的页 ...

  3. 微信扫码支付模式二【无法回调】解决方案(转)

    微信扫码支付模式二[无法回调]解决方案(转) 参考文章: (1)微信扫码支付模式二[无法回调]解决方案(转) (2)https://www.cnblogs.com/kenshinobiy/p/8724 ...

  4. 随手记录JAVA微信扫码支付模式二功能

    概述 本例子实现微信扫码支付模式二的支付功能,应用场景是,web网站微信扫码支付.实现从点击付费按钮.到弹出二维码.到用户用手机微信扫码支付.到手机上用户付费成功.web网页再自动调整到支付成功后的页 ...

  5. 微信 扫码支付模式二 开发

    概要 主要记录自己的开发流程,使用的springMVC 支付流程 引入相关依赖 <!-- 微信支付 --> <dependency><groupId>com.git ...

  6. 微信扫码支付dome php,帝国cms 微信扫码支付 模式二 扫码付款demo

    [实例简介] 到处没找到帝国扫码支付的插件,找到的都是收费的.所以自己随便改了个扫码支付,官方DEMO改的.功能都已实现.支付测试扫码的都没问题.后台支付记录也能正常添加 [实例截图] [核心代码] ...

  7. php微信支付使用ajax,微信扫码支付模式二支付状态Ajax轮询实例

    Ajax 轮训支付状态代码: //设置每隔1000毫秒执行一次load() 方法 setInterval(function(){load()},1000); function load(){ var ...

  8. 微信支付ajax实现支付,微信扫码支付模式二支付状态Ajax轮询实例

    Ajax 轮训支付状态代码: //设置每隔1000毫秒执行一次load() 方法 setInterval(function(){load()},1000); function load(){ var ...

  9. Java微信扫码支付(模式二)

    更多最新文章欢迎大家访问我的个人博客

  10. Java之微信支付(扫码支付模式二)案例实战

    摘要:最近的一个项目中涉及到了支付业务,其中用到了微信支付和支付宝支付,在做的过程中也遇到些问题,所以现在总结梳理一下,分享给有需要的人,也为自己以后回顾留个思路. 一:微信支付接入准备工作: 首先, ...

最新文章

  1. 程序员必备注释模板---佛祖保佑
  2. delete什么头文件C语言,C++中new和delete的介绍
  3. win32格式化错误消息
  4. http协议下:为什么请求与响应会做到准确误的对应。不会出现请求与响应的错乱...
  5. HDU多校1 - 6756 Finding a MEX(分块+二分+树状数组)
  6. 关于表单回车自动提交的讨论
  7. oracle优质图书,经典Oracle图书推荐(之四)_oracle
  8. codeigniter 操作 图标
  9. java 二次封装azkaban 实现azkaban任务的执行
  10. html id命名规范,关于Html class id 命名规范
  11. sqlserver中计算日期差
  12. Designing Data-Intensive Applications翻译
  13. 【短链接】——新浪、百度、搜狐等官方长链接转短链接
  14. 苹果圆圈怎么设置_苹果手机白色圆圈怎么弄
  15. 浅谈Python中的type()、dtype()、astype()的区别
  16. 刑侦 技侦 警种
  17. 思维导图分享以及MindManager使用说明
  18. VirtualBox无法安装增强工具-共享复制粘贴功能
  19. 计算机内存不足黑屏怎么办,win10内存不足会黑屏吗_win10电脑内存不足黑屏了怎么办...
  20. Ptr ds 与ptr ss

热门文章

  1. castep 编译安装说明
  2. 儿童编程:搭房子编程序-电脑小猫听我话
  3. 【UI插件】adeboXD切图插件-标记狮
  4. 脚手架创建项目vue2.0
  5. 网站实现微博登录自动关注微博的权限,scope权限!
  6. 怎样运营头条自媒体?学会这几招就行
  7. 背景音乐的相对路径html,给网站加上背景音乐的方法
  8. 数据库库设计:字段是否允许为空的思考
  9. 从来没有一种工作叫钱多、事少、离家近(转)
  10. 废水中氟离子去除方法