微信支付
微信支付的兩種模式
1:由微信生成二維碼,客戶掃描二維碼後,確認支付,微信回調給當前系統.
2:由系統調用統一下單API,取得預支付交易信息後,根據信息生成二維碼,然後後臺循環查詢訂單API接口查詢付款狀態(統一下單API,查詢訂單API)
區別:模式一是跳到微信讓微信給我們生成二維碼
模式二是可控制高,比較自主.
一:二維碼
1.1:二維碼優勢
♦ 信息量大,可以容納1850個大寫字母或者2710個數字或500多個漢字
♦ 應用範圍廣,支持文字,聲音(存放地址,地址裏播放聲音其實還是地址),圖片,指紋等等
♦ 容錯能力強,即是圖片出現部分的破損也能使用
♦ 成本低,容易製作
二:微信支付
2.1:一共12個API
實現思路:
微信接口只接收xml字符串需要sas解析或者dom4j解析.將map轉成xml通過httpClient遠程提交參數接收返回結果
微信接口提供了一些工具類把map轉換成xml
統一下單接口最終就是通過它生成一串支付地址,然後把支付地址展示給前端頁面,讓用戶支付.最終訂單有沒有完成,需要調用查詢訂單接口,查看完成狀態.
微信支付發送的是post請求,請求是xml格式的
代碼實現
一:需要創建支付服務模塊(pyg_pay_interface和pyg_pay_service)
**A?*pyg_pay_service
1:pom.xml文件導入座標
//打包方式爲war包爲web項目
<packaging>war</packaging>
<dependencies>
//導入工具類依賴
<dependency>
<groupId>com.pinyougou</groupId>
<artifactId>pyg_common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
//依賴dao層操作數據庫
<dependency>
<groupId>com.pinyougou</groupId>
<artifactId>pyg_dao</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
//依賴於pyg_pay_interface接口
<dependency>
<groupId>com.pinyougou</groupId>
<artifactId>pyg_pay_interface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
//微信的座標,可以使用它的工具類生成uuid
<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>0.0.3</version>
</dependency>
//指定一臺服務器
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<!-- 指定端口 -->
<port>9000</port>
<!-- 請求路徑 -->
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
</dependencies>
2:web.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<!-- 加載spring容器 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring/applicationContext*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
3.1:applicationContext-service.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 訪問dubbo要佔用的當前主機端口,默認端口不寫是20880 -->
<dubbo:protocol name="dubbo" port="20880"></dubbo:protocol>
<!-- 配置dubbo服務的名稱 -->
<dubbo:application name="pinyougou-pay-service"/>
<!-- 配置dubbo註冊地址-->
<dubbo:registry address="zookeeper://192.168.25.134:2181"/>
<!--配置dubbo服務註解掃描的包 -->
<dubbo:annotation package="com.pinyougou.pay.service.impl" />
</beans>
3.2:applicationContext-tx.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 事務管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 開啓事務控制的註解支持 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
4.在pyg_common導入配置文件
支付相關的信息
appid=wx8397f8696b538317 //商戶ID
partner=1473426802 //商戶號
partnerkey=T6m9iK73b0kn9g5v426MKfHQH7X8rKwb //商戶的key值
notifyurl=http://a31ef7db.ngrok.io/WeChatPay/WeChatPayNotify //回調地址,我們的二維碼使我們自己生成的所以不需要回調
導入HttpClientUtils.java工具類
package util;
/**
* http請求客戶端
*
* @author Administrator
*
*/
public class HttpClientUtil {
public static HttpClientContext context = null;
static {
System.out.println("====================begin");
context = HttpClientContext.create();
}
private String url;
private Map<String, String> param;
private int statusCode;
private String content;
private String xmlParam;
private boolean isHttps;
public boolean isHttps() {
return isHttps;
}
public void setHttps(boolean isHttps) {
this.isHttps = isHttps;
}
public String getXmlParam() {
return xmlParam;
}
public void setXmlParam(String xmlParam) {
this.xmlParam = xmlParam;
}
public HttpClientUtil(String url, Map<String, String> param) {
this.url = url;
this.param = param;
}
public HttpClientUtil(String url) {
this.url = url;
}
public void setParameter(Map<String, String> map) {
param = map;
}
public void addParameter(String key, String value) {
if (param == null)
param = new HashMap<String, String>();
param.put(key, value);
}
public void post() throws ClientProtocolException, IOException {
HttpPost http = new HttpPost(url);
setEntity(http);
execute(http);
}
public void put() throws ClientProtocolException, IOException {
HttpPut http = new HttpPut(url);
setEntity(http);
execute(http);
}
public void get() throws ClientProtocolException, IOException {
if (param != null) {
StringBuilder url = new StringBuilder(this.url);
boolean isFirst = true;
for (String key : param.keySet()) {
if (isFirst)
url.append("?");
else
url.append("&");
url.append(key).append("=").append(param.get(key));
}
this.url = url.toString();
}
HttpGet http = new HttpGet(url);
execute(http);
}
/**
* set http post,put param
*/
private void setEntity(HttpEntityEnclosingRequestBase http) {
if (param != null) {
List<NameValuePair> nvps = new LinkedList<NameValuePair>();
for (String key : param.keySet())
nvps.add(new BasicNameValuePair(key, param.get(key))); // 參數
http.setEntity(new UrlEncodedFormEntity(nvps, Consts.UTF_8)); // 設置參數
}
if (xmlParam != null) {
http.setEntity(new StringEntity(xmlParam, Consts.UTF_8));
}
}
private void execute(HttpUriRequest http) throws ClientProtocolException,
IOException {
CloseableHttpClient httpClient = null;
try {
if (isHttps) {
SSLContext sslContext = new SSLContextBuilder()
.loadTrustMaterial(null, new TrustStrategy() {
// 信任所有
public boolean isTrusted(X509Certificate[] chain,
String authType)
throws CertificateException {
return true;
}
}).build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslContext);
httpClient = HttpClients.custom().setSSLSocketFactory(sslsf)
.build();
} else {
httpClient = HttpClients.createDefault();
}
CloseableHttpResponse response = httpClient.execute(http,context);
try {
if (response != null) {
if (response.getStatusLine() != null)
statusCode = response.getStatusLine().getStatusCode();
HttpEntity entity = response.getEntity();
// 響應內容
content = EntityUtils.toString(entity, Consts.UTF_8);
}
} finally {
response.close();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
httpClient.close();
}
}
public int getStatusCode() {
return statusCode;
}
public String getContent() throws ParseException, IOException {
return content;
}
}
導入生成支付二維碼的頁面
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=9; IE=8; IE=7; IE=EDGE">
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />
<title>微信支付頁</title>
<link rel="icon" href="/assets/img/favicon.ico">
<script type="text/javascript" src="plugins/angularjs/angular.min.js"> </script>
<script type="text/javascript" src="js/base.js"> </script>
<script type="text/javascript" src="js/service/payService.js"> </script>
<script type="text/javascript" src="js/controller/payController.js"> </script>
<script type="text/javascript" src="plugins/qrious.min.js"></script>
<link rel="stylesheet" type="text/css" href="css/webbase.css" />
<link rel="stylesheet" type="text/css" href="css/pages-weixinpay.css" />
</head>
<body ng-app="pinyougou" ng-controller="payController" ng-init="createNative()">
<!--head-->
<div class="top">
<div class="py-container">
<div class="shortcut">
<ul class="fl">
<li class="f-item">品優購歡迎您!</li>
<li class="f-item">請登錄 <span><a href="#">免費註冊</a></span></li>
</ul>
<ul class="fr">
<li class="f-item">我的訂單</li>
<li class="f-item space"></li>
<li class="f-item">我的品優購</li>
<li class="f-item space"></li>
<li class="f-item">品優購會員</li>
<li class="f-item space"></li>
<li class="f-item">企業採購</li>
<li class="f-item space"></li>
<li class="f-item">關注品優購</li>
<li class="f-item space"></li>
<li class="f-item">客戶服務</li>
<li class="f-item space"></li>
<li class="f-item">網站導航</li>
</ul>
</div>
</div>
</div>
<div class="cart py-container">
<!--logoArea-->
<div class="logoArea">
<div class="fl logo"><span class="title">收銀臺</span></div>
</div>
<!--主內容-->
<div class="checkout py-container pay">
<div class="checkout-tit">
<h4 class="fl tit-txt"><span class="success-icon"></span><span class="success-info">訂單提交成功,請您及時付款!訂單號:{{payNo}}</span></h4>
<span class="fr"><em class="sui-lead">應付金額:</em><em class="orange money">¥{{payMoney}}</em>元</span>
<div class="clearfix"></div>
</div>
<div class="checkout-steps">
<div class="fl weixin">微信支付</div>
<div class="fl sao">
<p class="red">二維碼已過期,刷新頁面重新獲取二維碼。</p>
<div class="fl code">
<img id="qrious" alt="">
<div class="saosao">
<p>請使用微信掃一掃</p>
<p>掃描二維碼支付</p>
</div>
</div>
<div class="fl phone">
</div>
</div>
<div class="clearfix"></div>
<p><a href="pay.html" target="_blank">> 其他支付方式</a></p>
</div>
</div>
</div>
<!-- 底部欄位 -->
<!--頁面底部-->
<div class="clearfix footer">
<div class="py-container">
<div class="footlink">
<div class="Mod-service">
<ul class="Mod-Service-list">
<li class="grid-service-item intro intro1">
<i class="serivce-item fl"></i>
<div class="service-text">
<h4>正品保障</h4>
<p>正品保障,提供發票</p>
</div>
</li>
<li class="grid-service-item intro intro2">
<i class="serivce-item fl"></i>
<div class="service-text">
<h4>正品保障</h4>
<p>正品保障,提供發票</p>
</div>
</li>
<li class="grid-service-item intro intro3">
<i class="serivce-item fl"></i>
<div class="service-text">
<h4>正品保障</h4>
<p>正品保障,提供發票</p>
</div>
</li>
<li class="grid-service-item intro intro4">
<i class="serivce-item fl"></i>
<div class="service-text">
<h4>正品保障</h4>
<p>正品保障,提供發票</p>
</div>
</li>
<li class="grid-service-item intro intro5">
<i class="serivce-item fl"></i>
<div class="service-text">
<h4>正品保障</h4>
<p>正品保障,提供發票</p>
</div>
</li>
</ul>
</div>
<div class="clearfix Mod-list">
<div class="yui3-g">
<div class="yui3-u-1-6">
<h4>購物指南</h4>
<ul class="unstyled">
<li>購物流程</li>
<li>會員介紹</li>
<li>生活旅行/團購</li>
<li>常見問題</li>
<li>購物指南</li>
</ul>
</div>
<div class="yui3-u-1-6">
<h4>配送方式</h4>
<ul class="unstyled">
<li>上門自提</li>
<li>211限時達</li>
<li>配送服務查詢</li>
<li>配送費收取標準</li>
<li>海外配送</li>
</ul>
</div>
<div class="yui3-u-1-6">
<h4>支付方式</h4>
<ul class="unstyled">
<li>貨到付款</li>
<li>在線支付</li>
<li>分期付款</li>
<li>郵局匯款</li>
<li>公司轉賬</li>
</ul>
</div>
<div class="yui3-u-1-6">
<h4>售後服務</h4>
<ul class="unstyled">
<li>售後政策</li>
<li>價格保護</li>
<li>退款說明</li>
<li>返修/退換貨</li>
<li>取消訂單</li>
</ul>
</div>
<div class="yui3-u-1-6">
<h4>特色服務</h4>
<ul class="unstyled">
<li>奪寶島</li>
<li>DIY裝機</li>
<li>延保服務</li>
<li>品優購E卡</li>
<li>品優購通信</li>
</ul>
</div>
<div class="yui3-u-1-6">
<h4>幫助中心</h4>
<img src="img/wx_cz.jpg">
</div>
</div>
</div>
<div class="Mod-copyright">
<ul class="helpLink">
<li>關於我們<span class="space"></span></li>
<li>聯繫我們<span class="space"></span></li>
<li>關於我們<span class="space"></span></li>
<li>商家入駐<span class="space"></span></li>
<li>營銷中心<span class="space"></span></li>
<li>友情鏈接<span class="space"></span></li>
<li>關於我們<span class="space"></span></li>
<li>營銷中心<span class="space"></span></li>
<li>友情鏈接<span class="space"></span></li>
<li>關於我們</li>
</ul>
<p>地址:北京市昌平區建材城西路金燕龍辦公樓一層 郵編:100096 電話:400-618-4000 傳真:010-82935100</p>
<p>京ICP備08001421號京公網安備110108007702</p>
</div>
</div>
</div>
</div>
<!--頁面底部END-->
<script type="text/javascript" src="js/plugins/jquery/jquery.min.js"></script>
<script type="text/javascript" src="js/plugins/jquery.easing/jquery.easing.min.js"></script>
<script type="text/javascript" src="js/plugins/sui/sui.min.js"></script>
<script type="text/javascript" src="js/widget/nav.js"></script>
<script type="text/javascript">
$(function(){
$("ul.payType li").click(function(){
$(this).css("border","2px solid #E4393C").siblings().css("border-color","#ddd");
})
})
</script>
</body>
</html>
5.根據Dubbo服務掃描的包創建包(com.pinyougou.pay.service.impl)
♦ 5.1:pyg_pay_interface模塊下創建PayService.java接口
public interface PayService{
//請求完微信生成支付地址的方法(二維碼)
//參數1:商戶訂單號 參數二:總金額
public Map createNative(String out_trade_no,String total_fee);
//請求微信查詢支付狀態
public Map queryPayStatus(String out_trade_no);
//根據用戶id獲取支付單對象
public TbPayLog searchPayLogFromRedis(String userId);
//根據支付單號,更新付款信息
public void updateOrderStatus(String out_trade_no,String transaction_id);
}
♦ 5.2:pyg_pay_service模塊com.pinyougou.pay.service.impl路徑下創建PayServiceImpl.java實現類
@Service
public class PayServiceImpl implement PayService{
//拿到配置文件的屬性
@Value("${appid}")
private String appid;//商戶ID
@Value("${partner}")
private String partner;//商家號
@Value("${partnerkey}")
private String partnerkey;//祕鑰
@Value("${notifyurl}")
private String notifyurl;//回調地址(必填,內容隨便)
@Autowride
private RedisTemplate redisTemplate;
@Autowride
private TbPayLogMapper payLogMapper;
@Autowride
private TborderMapper orderMapper;
@Override
public Map createNative(String out_trade_no,String total_fee){
try{//請求微信的生成訂單接口地址,獲取支付的連接地址
HttpClientUtils util=new HttpClientUtils("https://api.mch.weixin.qq.com/pay/unifiedorder");
//請求內容數據的封裝
Map map= new HashMap<>();
map.put("appid",appid);
map.put("mch_id",partner);
map.put("nonce_str",WXPayUtil.generateUUID());//隨機32位字符串
map.put("body","品優購商品"); //描述信息
map.put("out_trade_no",out_trade_no);//商戶訂單號
double totalMoney=new Double(total_fee)*100;
map.put("total_fee",(long)totalMoney+"") //商品總金額單位爲分
map.put("spbill_create_ip","127.0.0.1"); //終端地址
map.put("notify_url",notifyurl); //通知地址
map.put("trade_type","NATIVE"); //交易類型
//通過微信的api將map轉成xml格式的字符串
//生成帶簽名的xml
String signedXml= WXPayUtil.generateSignedXml(map,partnerkey);
Sytem.out.print("發送給微信"+signedXml)
//設置請求參數setXmlParam();方法發送的是xml格式
util.setXmlParam(signedXml);
//發送post請求,發送的是xml格式的
util.post();
String content=util.getContent();
Sytem.out.print("微信返回的"+content)
//通過工具類將返回的String的xml內容轉換成map
Map responseMap=WXPayUtil.xmlToMap(content);
Map resultMap=new HashMap<>();
resultMap.put("out_trade_no",out_trade_no); //支付單號
resultMap.put("total_fee",total_fee); //支付金額
resultMap.put("code_url",responseMap.get("code_url"));//支付地址
return resultMap;
}catch(IOException e){
e.printStackTrace();
}
return null;
}
}
@Override
public Map queryPayStatus(String out_trade_no){
try{
//1.請求微信的訂單狀態查詢接口
HttpClient util new HttpClient("https://api.mch.weixin.qq.com/pay/orderquery");
//2.封裝數據
Map requestMap= new HashMap<>();
requestMap.put("appid",appid);
requestMap.put("mch_id",mch_id);
requestMap.put("out_trade_no",out_trade_no);
requestMap.put("nonce_str",WXPayUtil.generateUUID());
//3.轉換帶簽名的數據
String signedXml=WXPayUtil.generateSignedXml(requestMap,partnerkey);
//4.設置到xml格式到post請求中
util.setXmlParam(signedXml);
//5.發送請求(xml)
util.post();
//6.獲取返回值
String content= util.getContent();
Map resultMap= WXPayUtil.xmlToMap(content);
return resultMap;
}catch(Exception e){
e.printStackTrace();
return null;
}
}
@Override
public TbPayLog searchPayLogFromRedis(String userId){
TbPayLog payLog=(TbPayLog) redisTemplate.boundHashOps("payLog").get(userId);
return payLog;
}
@Override
//根據支付單號,更新付款信息
public void updateOrderStatus(String out_trade_no,String transaction_id){
//獲取支付單對象
TbPayLog payLog= payLogMapper.selectByPrimaryKey(out_trade_no);
//獲取支付狀態,微信的業務代碼
payLog.setTransactionId(transaction_id);
//支付時間
payLog.setPayTime(new Date());
//支付狀態
payLog.setTradeState("1");//0未支付 1已支付
//更新數據庫中的payLog數據
payLogMapper.updateByPrimaryKey(payLog);
//根據支付單的訂單列表,更新訂單支付的相關信息
String[] orderIds=payLog.getOrdrList().split(",");
for(String orderId :orderIds){
TbOrder order= orderMapper.selectByPrimaryKey(Long.parseLong(orderId);
//狀態:1 未付款 2 已付款
order.setStatus("2");
order.setPaymentTime(new Date());
order.setUpdateTime(new Date());
orderMapper.updateByPrimaryKey(order);
}
//將redis中的當前用戶的付款信息清除
redisTemplate.boundHash("payLog").delete(payLog.getUserId());
}
6.創建PayController.java類
@RestController
@RequestMapping("/pay")
public class PayController{
@Reference
private PayService payService;
@RequestMapping("/createNative")
public Map createNative(String out_trade_no,String total_fee){
/* String outTradeNo=UUID.randomUUID().toString().replaceAll("_","");*/
//獲取用戶的支付單
String userId=SecurityContextHolder.getContext().getAuthentication().getName();
//從Redis中獲取用戶的支付單
TbPayLog payLog= payService.searchPayLogFromRedis(userId);
//設置訂單號 付款金額
return payService.createNative(payLog.getOutTradeNo(),payLog.getTotalFee());
}
@RequestMapping("/queryPayStatus")
public Result queryPayStatus(String out_trade_no){
int timeout=1;
//無限循環查詢訂單支付狀態
while(true){
Map resultMap=payService.queryPayStatus(out_trade_no);
//失敗的邏輯
if(resultMap==null){
return new Result(false,"付款失敗!!!");
}
//支付成功的邏輯
if("SUCCESS".equals(resultMap.get("trade_state"))){
//修改order的狀態 修改paylog的狀態
payService.updateOrderStatus(out_trade_no,resultMap.get("transaction_id").toString());
return new Result(true,"付款成功!!!");
}
//每隔三秒查詢訂單的支付狀態
try{
Thread.sleep(3000);
}catch(Exception e){
}
timeout++;
//退出邏輯 ,超過該時間,停止支付查詢
if(timeout>10){
return new Result(false,"timeout");
}
}
}
}