标题
- 图解
- 扫码支付开发者文档
- 1·下载微信支付demo
- 2.提取微信支付工具类
- 微信支付异步通知链接
- 微信支付通知
- WXPaymentController
- WXPayConfig
- MyWXPayRequest
- service
图解
扫码支付开发者文档
模式二与模式一相比,流程更为简单,不依赖设置的回调支付URL。商户后台系统先调用微信支付的统一下单接口,微信后台系统返回链接参数code_url,商户后台系统将code_url值生成二维码图片,用户使用微信客户端扫码后发起支付。注意:code_url有效期为2小时,过期后扫码不能再发起支付。
1·下载微信支付demo
2.提取微信支付工具类
新建trade模块 弄好依赖关系 建包如下
并且trade模块pom.xml在增加个依赖
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.3</version>
</dependency>
3.编写微信支付调用接口
4.生成微信支付二维码
5.编写微信异步通知接口
6.编写定时程序检测微信支付结果订单号必须填对
下面通过草料二维码生成二维码 用手机扫一扫
用浏览器来测试
效果一样
微信支付异步通知链接
微信支付通知
整体模块如下
WXPaymentController
package cn.itrip.trade.controller;
import cn.itrip.beans.dto.Dto;
import cn.itrip.beans.pojo.ItripHotelOrder;
import cn.itrip.trade.config.WXPayConfig;
import cn.itrip.trade.service.OrderService;
import cn.itrip.trade.wx.MyWXPayRequest;
import cn.itrip.trade.wx.WXPayConstants;
import cn.itrip.trade.wx.WXPayUtil;
import cn.itrip.util.common.DtoUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
@Controller
@RequestMapping("/api/wxpay")
public class WXPaymentController {
static Logger logger = LoggerFactory.getLogger(WXPaymentController.class);
@Resource
private OrderService orderService;
@Resource
private WXPayConfig wxPayConfig;
/**文档参数地址 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
* get
* http://localhost:8080/wx//api/wxpay/createcode/D100000120200602144804b01129
* 生成微信支付二维码的方法
*/
@RequestMapping(value = "/createcode/{orderNo}", method = RequestMethod.GET)
@ResponseBody
public Dto createCode(@PathVariable String orderNo) throws Exception {
try {
//根据订单编号查询订单信息
ItripHotelOrder order = orderService.loadItripHotelOrder(orderNo);
//1、构造参数
Map<String, String> data = new HashMap<String, String>();
//商品描述 body
data.put("body", "爱旅行项目订单支付");
//商户订单号 out_trade_no
data.put("out_trade_no", orderNo);
//设备号 device_info
data.put("device_info", "");
//标价币种 fee_type 示例值 CNY(默认人民币)
data.put("fee_type", "CNY");
//下面测试 用1分钱来测试(订单总金额,单位为分)标价金额 total_fee
//data.put("total_fee", "1");//不能这么写 竟然postman测试报错下面可以
data.put("total_fee", order.getPayAmount().multiply(new BigDecimal(100)).toBigInteger().toString());
// 终端IP spbill_create_ip
data.put("spbill_create_ip", "47.92.146.135");
// data.put("notify_url", wxPayConfig.getNotifyUrl());
//不知道我的为啥得用官方文档这块给的这个值才行 按理说上面可以的吧毕竟弄到配置文件里了
data.put("notify_url", "http://www.weixin.qq.com/wxpay/pay.php");
//交易类型 trade_type JSAPI -JSAPI支付NATIVE -Native支付//APP -APP支付
data.put("trade_type", "NATIVE");
//商品ID product_id trade_type=NATIVE时,此参数必传。此参数为二维码中包含的商品ID,商户自行定义。
data.put("product_id", "12");
//公众账号ID appid 微信支付分配的公众账号ID(企业号corpid即为此appId)
data.put("appid", wxPayConfig.getAppID());
//商户号 mch_id 微信支付分配的商户号
data.put("mch_id", wxPayConfig.getMchID());
//签名类型 sign_type 签名类型,默认为MD5,支持HMAC-SHA256和MD5。
data.put("sign_type", "HMAC-SHA256");
//随机字符串 nonce_str 微信返回的随机字符串
data.put("nonce_str", WXPayUtil.generateNonceStr());
// todo 注:参数值用XML转义即可,CDATA标签用于说明数据不被XML解析器解析。
//2、把参数转换成XML格式,请求微信支付平台
//生成签名
String reqXml = WXPayUtil.generateSignedXml(
data, wxPayConfig.getKey(),
WXPayConstants.SignType.HMACSHA256);
//3、请求微信支付平台,获取与支付交易链接
//支付 接口链接
//URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder
//URL地址:https://api2.mch.weixin.qq.com/pay/unifiedorder(备用域名)见跨城冗灾方案 比上面多个s https
String respXML = MyWXPayRequest.requestWX("https://api.mch.weixin.qq.com/pay/unifiedorder", reqXml);
//xml转换为map
Map<String, String> resultMap = WXPayUtil.xmlToMap(respXML);
//判断是否请求成功
//以下字段在return_code为SUCCESS的时候有返回
//公众账号ID 商户号 设备号 随机字符串 签名 业务结果 错误代码 错误代码描述
//以下字段在return_code 和result_code都为SUCCESS的时候有返回
//交易类型 trade_type 预支付交易会话标识 prepay_id 二维码链接 code_url
if (resultMap.get("return_code").equals("SUCCESS")
&& resultMap.get("result_code").equals("SUCCESS")) {
Map<String, String> result = new HashMap<>();
//trade_type=NATIVE时有返回,此url用于生成支付二维码,然后提供给用户进行扫码支付。
//注意:code_url的值并非固定,使用时按照URL格式转成二维码即可
result.put("code_url", resultMap.get("code_url"));
return DtoUtil.returnDataSuccess(result);
} else {
return DtoUtil.returnFail(resultMap.get("return_msg"), "110002");
}
} catch (Exception e) {
e.printStackTrace();
return DtoUtil.returnFail("订单运行异常", "110003");
}
}
//回调函数
@RequestMapping("/notify")
public void wxNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
logger.info("成功被调用");
try {
//1、从request获取XML流
StringBuffer dataBuf = new StringBuffer();
InputStream is = request.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is, "utf-8"));
//
String temp;
while ((temp = reader.readLine()) != null) {
dataBuf.append(temp);
}
reader.close();
is.close();
//2、 识别是否成功,修改订单状态
//xml-->map
Map<String, String> resultMap = WXPayUtil.xmlToMap(dataBuf.toString());
boolean valid = WXPayUtil.isSignatureValid(
resultMap,
wxPayConfig.getKey(),
WXPayConstants.SignType.HMACSHA256);
//数据合法化验证:是否是微信返回的
if (valid) {
logger.info("签名验证通过");
//判断是否请求成功
if (resultMap.get("return_code").equals("SUCCESS")
&& resultMap.get("result_code").equals("SUCCESS")) {
logger.info("订单支付成功");
//
String out_trade_no = resultMap.get("out_trade_no");
String trade_no = resultMap.get("prepay_id");
//订单状态
if (!orderService.processed(out_trade_no)) {
//订单未处理,则进行支付
orderService.paySuccess(out_trade_no, 2, trade_no);
}
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
logger.info("成功返回数据");
//3、给微信返回结果
Map<String, String> returnMap = new HashMap<String, String>();
returnMap.put("return_code", "SUCCESS");
returnMap.put("return_msg", "SUCCESS");
try {
String respXml = WXPayUtil.generateSignedXml(returnMap, "2ab9071b06b9f739b950ddb41db2690d");
response.getWriter().write(respXml);
response.getWriter().flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 定时查询订单状态,刷新订单
*/
@RequestMapping(value = "/queryorderstatus/{orderNo}", method = RequestMethod.GET)
@ResponseBody
public Dto queryOrderStatus(@PathVariable String orderNo) throws Exception {
try {
ItripHotelOrder order = orderService.loadItripHotelOrder(orderNo);
return DtoUtil.returnDataSuccess(order);
} catch (Exception e) {
e.printStackTrace();
}
return DtoUtil.returnFail("查询失败", "100003");
}
}
WXPayConfig
package cn.itrip.trade.config;
public class WXPayConfig {
private String appID;
private String mchID;
private String key;
private String notifyUrl;
private String successUrl;
private String failUrl;
public String getAppID() {
return appID;
}
public void setAppID(String appID) {
this.appID = appID;
}
public String getMchID() {
return mchID;
}
public void setMchID(String mchID) {
this.mchID = mchID;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getNotifyUrl() {
return notifyUrl;
}
public void setNotifyUrl(String notifyUrl) {
this.notifyUrl = notifyUrl;
}
public String getSuccessUrl() {
return successUrl;
}
public void setSuccessUrl(String successUrl) {
this.successUrl = successUrl;
}
public String getFailUrl() {
return failUrl;
}
public void setFailUrl(String failUrl) {
this.failUrl = failUrl;
}
}
MyWXPayRequest
package cn.itrip.trade.wx;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import java.util.Map;
public class MyWXPayRequest {
public static String requestWX(String url,String data) throws Exception{
BasicHttpClientConnectionManager connManager;
connManager = new BasicHttpClientConnectionManager(
RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", SSLConnectionSocketFactory.getSocketFactory())
.build(),
null,
null,
null
);
HttpClient httpClient = HttpClientBuilder.create()
.setConnectionManager(connManager)
.build();
HttpPost httpPost = new HttpPost(url);
RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(1000).setConnectTimeout(1000)
.build();
httpPost.setConfig(requestConfig);
StringEntity postEntity = new StringEntity(data, "UTF-8");
httpPost.addHeader("Content-Type", "text/xml");
httpPost.setEntity(postEntity);
try {
HttpResponse httpResponse = httpClient.execute(httpPost);
HttpEntity httpEntity = httpResponse.getEntity();
return EntityUtils.toString(httpEntity, "UTF-8");
}catch (Exception e){
e.printStackTrace();
}
return null;
}
}
service
package cn.itrip.trade.service;
import cn.itrip.beans.pojo.ItripHotelOrder;
public interface OrderService {
/**
* 加载酒店订单
* @param orderNo
* @return
* @throws Exception
*/
public ItripHotelOrder loadItripHotelOrder(String orderNo) throws Exception;
/**
* 判断该订单是否已被处理过(被更新为已支付状态)
* @param orderNo
* @return
* @throws Exception
*/
public boolean processed(String orderNo) throws Exception;
/**
* 支付成功
* @param orderNo 订单编号
* @param payType 支付方式:1:支付宝 2:微信 3:到店付
* @param tradeNo 支付平台返回的交易码
* @throws Exception
*/
public void paySuccess(String orderNo, int payType, String tradeNo) throws Exception;
}
package cn.itrip.trade.service;
import cn.itrip.beans.pojo.ItripHotelOrder;
import cn.itrip.beans.pojo.ItripTradeEnds;
import cn.itrip.dao.hotelorder.ItripHotelOrderMapper;
import cn.itrip.dao.tradeends.ItripTradeEndsMapper;
import cn.itrip.util.common.EmptyUtils;
import cn.itrip.util.common.SystemConfig;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.BufferedReader;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Service("orderService")
public class OrderServiceImpl implements OrderService {
@Resource
private ItripHotelOrderMapper itripHotelOrderMapper;
@Resource
private SystemConfig systemConfig;
@Resource
private ItripTradeEndsMapper itripTradeEndsMapper;
@Override
public ItripHotelOrder loadItripHotelOrder(String orderNo) throws Exception {
Map<String, Object> param = new HashMap<>();
param.put("orderNo", orderNo);
List<ItripHotelOrder> orders = itripHotelOrderMapper.getItripHotelOrderListByMap(param);
if (orders.size() == 1) {
return orders.get(0);
} else {
return null;
}
}
@Override
public boolean processed(String orderNo) throws Exception {
ItripHotelOrder order = this.loadItripHotelOrder(orderNo);
return order.getOrderStatus().equals(2)&& EmptyUtils.isNotEmpty(order.getTradeNo());
}
@Override
public void paySuccess(String orderNo, int payType, String tradeNo) throws Exception {
//更新订单状态、支付宝交易号
ItripHotelOrder itripHotelOrder=this.loadItripHotelOrder(orderNo);
itripHotelOrder.setOrderStatus(2);//支付成功
itripHotelOrder.setPayType(payType);
itripHotelOrder.setTradeNo(tradeNo);//交易号(如支付宝交易号)
itripHotelOrderMapper.updateItripHotelOrder(itripHotelOrder);
//增加订单后续待处理记录
ItripTradeEnds itripTradeEnds=new ItripTradeEnds();
itripTradeEnds.setId(itripHotelOrder.getId());
itripTradeEnds.setOrderNo(itripHotelOrder.getOrderNo());
itripTradeEndsMapper.insertItripTradeEnds(itripTradeEnds);
//通知业务模块后续处理
sendGet(systemConfig.getTradeEndsUrl(),"orderNo="+orderNo);
}
public void sendGet(String url, String param) {
String result = "";
BufferedReader in = null;
try {
String urlNameString = url + "?" + param;
URL realUrl = new URL(urlNameString);
// 打开和URL之间的连接
URLConnection connection;
if(systemConfig.getTradeUseProxy()){//代理环境
Proxy proxy = new Proxy(Proxy.Type.HTTP,new InetSocketAddress(
systemConfig.getTradeProxyHost(),
systemConfig.getTradeProxyPort()));
connection= realUrl.openConnection(proxy);
}
else{
connection= realUrl.openConnection();
}
// 设置通用的请求属性
connection.setRequestProperty("accept", "*/*");
connection.setRequestProperty("connection", "Keep-Alive");
connection.setRequestProperty("user-agent",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
// 建立实际的连接
connection.connect();
System.out.println(connection.getContentLength());
} catch (Exception e) {
e.printStackTrace();
}
}
}