PYG电商项目开发 -- day13 订单、微信二维码支付

一、结算页面显示订单信息


1、加入html页面




2、改造getOrderInfo.html




3、orderController.js


//定义控制器
app.controller("orderController", function($scope, cartService, addressService, orderService) {
	
	/**
	 * 页面加载展示购物车中数据
	 */
	$scope.findCartList = function(){
		cartService.findCartList().success(function(data){
			$scope.cartList = data;
			//计算页面需要展示的其他数据
			$scope.totalNum = 0; //总数量
			$scope.totalFee = 0.00; //总价格
			for (var i = 0; i < data.length; i++) {
				var orderItemList = data[i].orderItems;
				for (var j = 0; j < orderItemList.length; j++) {
					$scope.totalNum += orderItemList[j].num;
					$scope.totalFee += orderItemList[j].totalFee;
				}
			}
		});
	}

	//初始化选中的收货人地址
	$scope.selectAddress = null;
	
	/**
	 * 查询用户地址列表
	 */
	$scope.findAddressByUser = function(){
		addressService.findAddressByUser().success(function(data){
			$scope.addressList = data;
			//设置默认选中的地址
			for (var i = 0; i < data.length; i++) {
				if(data[i].isDefault == '1'){
					$scope.selectAddress = data[i];
					break;
				}
			}
			if($scope.selectAddress == null && data.length > 0){
				$scope.selectAddress = data[0];
			}
		});
	};
	
	/**
	 * 判断当前地址是否是默认地址
	 */
	$scope.isSelectedAddress = function(pojo){
		if($scope.selectAddress.id == pojo.id){
			return true;
		}else{
			return false;
		}
	}
	
	/**
	 * 更改收货地址
	 */
	$scope.changeAddress = function(pojo){
		$scope.selectAddress = pojo;
	}
	
	
});


4、addressService.js


//服务层
app.service('addressService',function($http){
	
	//根据用户获取地址列表
	this.findAddressByUser = function(){
		return $http.get("../address/findAddressByUser.do");
	}
	    	
});


5、AddressController.java


package com.pinyougou.cart.controller;

import java.util.List;

import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.alibaba.dubbo.config.annotation.Reference;
import com.pinyougou.cart.service.AddressService;
import com.pinyougou.pojo.TbAddress;

/**
 * controller
 * 
 * @author Administrator
 *
 */
@RestController
@RequestMapping("/address")
public class AddressController {

	@Reference
	private AddressService addressService;

	/**
	 * 返回当前登录人地址列表
	 * 
	 * @return
	 */
	@RequestMapping("findAddressByUser")
	public List<TbAddress> findAddressByUser() {
		String username = SecurityContextHolder.getContext().getAuthentication().getName();
		return addressService.findAddressByUser(username);
	}

}


6、AddressServiceImpl.java


package com.pinyougou.cart.service.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;

import com.alibaba.dubbo.config.annotation.Service;
import com.pinyougou.cart.service.AddressService;
import com.pinyougou.mapper.TbAddressMapper;
import com.pinyougou.pojo.TbAddress;
import com.pinyougou.pojo.TbAddressExample;

/**
 * 服务实现层
 * 
 * @author Administrator
 *
 */
@Service
public class AddressServiceImpl implements AddressService {

	@Autowired
	private TbAddressMapper addressMapper;

	public List<TbAddress> findAddressByUser(String username) {
		TbAddressExample example = new TbAddressExample();
		example.createCriteria().andUserIdEqualTo(username);
		return addressMapper.selectByExample(example);
	}

}


二、结算按钮提交订单操作的数据准备以及js方法


1、getOrderInfo.html




2、orderController.js


	//初始化订单信息
	$scope.entity = {paymentType:'1', sourceType:'2'};
	
	/**
	 * 修改付款方式的方法
	 */
	$scope.changePaymentType = function(paymentType){
		$scope.entity['paymentType'] = paymentType;
	}
	
	
	/**
	 * 提交订单
	 */
	$scope.saveOrder = function(){
		$scope.entity['receiverAreaName'] = $scope.selectAddress.address;
		$scope.entity['receiverMobile'] = $scope.selectAddress.mobile;
		$scope.entity['receiver'] = $scope.selectAddress.contact;
		
		orderService.add($scope.entity).success(function(data){
			if(data.success){
				//跳转到支付页面
			}else{
				alert(data.message);
			}
		});
	}


3、orderService.js


/*用户的service层代码*/
app.service("orderService", function($http){
	
	//保存订单
	this.add = function(order){
		return $http.post("./order/saveOrder.do", order);
	}
});


三、搭建订单工程,实现提交订单(结算)后保存订单功能


1、搭建pinyougou-order-interface、order-service


项目搭建参考cart模块搭建过程以及配置


2、common项目中加入生成id的工具类


package utils;

import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.NetworkInterface;

/**
 * <p>名称:IdWorker.java</p>
 * <p>描述:分布式自增长ID</p>
 * <pre>
 *     Twitter的 Snowflake JAVA实现方案
 * </pre>
 * 核心代码为其IdWorker这个类实现,其原理结构如下,我分别用一个0表示一位,用—分割开部分的作用:
 * 1||0---0000000000 0000000000 0000000000 0000000000 0 --- 00000 ---00000 ---000000000000
 * 在上面的字符串中,第一位为未使用(实际上也可作为long的符号位),接下来的41位为毫秒级时间,
 * 然后5位datacenter标识位,5位机器ID(并不算标识符,实际是为线程标识),
 * 然后12位该毫秒内的当前毫秒内的计数,加起来刚好64位,为一个Long型。
 * 这样的好处是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由datacenter和机器ID作区分),
 * 并且效率较高,经测试,snowflake每秒能够产生26万ID左右,完全满足需要。
 * <p>
 * 64位ID (42(毫秒)+5(机器ID)+5(业务编码)+12(重复累加))
 *
 * @author Polim
 */
public class IdWorker {
    // 时间起始标记点,作为基准,一般取系统的最近时间(一旦确定不能变动)
    private final static long twepoch = 1288834974657L;
    // 机器标识位数
    private final static long workerIdBits = 5L;
    // 数据中心标识位数
    private final static long datacenterIdBits = 5L;
    // 机器ID最大值
    private final static long maxWorkerId = -1L ^ (-1L << workerIdBits);
    // 数据中心ID最大值
    private final static long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    // 毫秒内自增位
    private final static long sequenceBits = 12L;
    // 机器ID偏左移12位
    private final static long workerIdShift = sequenceBits;
    // 数据中心ID左移17位
    private final static long datacenterIdShift = sequenceBits + workerIdBits;
    // 时间毫秒左移22位
    private final static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;

    private final static long sequenceMask = -1L ^ (-1L << sequenceBits);
    /* 上次生产id时间戳 */
    private static long lastTimestamp = -1L;
    // 0,并发控制
    private long sequence = 0L;

    private final long workerId;
    // 数据标识id部分
    private final long datacenterId;

    public IdWorker(){
        this.datacenterId = getDatacenterId(maxDatacenterId);
        this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);
    }
    /**
     * @param workerId
     *            工作机器ID
     * @param datacenterId
     *            序列号
     */
    public IdWorker(long workerId, long datacenterId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
        }
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
        }
        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }
    /**
     * 获取下一个ID
     *
     * @return
     */
    public synchronized long nextId() {
        long timestamp = timeGen();
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
        }

        if (lastTimestamp == timestamp) {
            // 当前毫秒内,则+1
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                // 当前毫秒内计数满了,则等待下一秒
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0L;
        }
        lastTimestamp = timestamp;
        // ID偏移组合生成最终的ID,并返回ID
        long nextId = ((timestamp - twepoch) << timestampLeftShift)
                | (datacenterId << datacenterIdShift)
                | (workerId << workerIdShift) | sequence;

        return nextId;
    }

    private long tilNextMillis(final long lastTimestamp) {
        long timestamp = this.timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = this.timeGen();
        }
        return timestamp;
    }

    private long timeGen() {
        return System.currentTimeMillis();
    }

    /**
     * <p>
     * 获取 maxWorkerId
     * </p>
     */
    protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) {
        StringBuffer mpid = new StringBuffer();
        mpid.append(datacenterId);
        String name = ManagementFactory.getRuntimeMXBean().getName();
        if (!name.isEmpty()) {
         /*
          * GET jvmPid
          */
            mpid.append(name.split("@")[0]);
        }
      /*
       * MAC + PID 的 hashcode 获取16个低位
       */
        return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
    }

    /**
     * <p>
     * 数据标识id部分
     * </p>
     */
    protected static long getDatacenterId(long maxDatacenterId) {
        long id = 0L;
        try {
            InetAddress ip = InetAddress.getLocalHost();
            NetworkInterface network = NetworkInterface.getByInetAddress(ip);
            if (network == null) {
                id = 1L;
            } else {
                byte[] mac = network.getHardwareAddress();
                id = ((0x000000FF & (long) mac[mac.length - 1])
                        | (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;
                id = id % (maxDatacenterId + 1);
            }
        } catch (Exception e) {
            System.out.println(" getDatacenterId: " + e.getMessage());
        }
        return id;
    }


}


3、在common的spring配置文件中初始化工具类


   <!-- 初始化雪花算法的工具类 -->
   <bean id="idWorker" class="utils.IdWorker"></bean>


4、OrderController.java


package com.pinyougou.cart.controller;

import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.alibaba.dubbo.config.annotation.Reference;
import com.pinyougou.order.service.OrderService;
import com.pinyougou.pojo.TbOrder;
import com.resultentity.ResultMessage;

@RestController
@RequestMapping("/order")
public class OrderController {

	@Reference
	private OrderService orderService;
	
	@RequestMapping("saveOrder")
	public ResultMessage saveOrder(@RequestBody TbOrder order) {
		//获取登录人id
		String userId = SecurityContextHolder.getContext().getAuthentication().getName();
		try {
			order.setUserId(userId);
			orderService.saveOrder(order);
			return new ResultMessage(true, "");
		} catch (Exception e) {
			e.printStackTrace();
			return new ResultMessage(false, "订单提交失败!!!");
		}
	}
	
}


5、OrderServiceImpl.java


package com.pinyougou.order.service.impl;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.dubbo.config.annotation.Service;
import com.pinyougou.mapper.TbOrderItemMapper;
import com.pinyougou.mapper.TbOrderMapper;
import com.pinyougou.order.service.OrderService;
import com.pinyougou.pojo.TbOrder;
import com.pinyougou.pojo.TbOrderItem;
import com.resultentity.Cart;

import utils.IdWorker;

@Service
@Transactional
public class OrderServiceImpl implements OrderService {
	@Autowired
	private IdWorker idWork;

	@Autowired
	private RedisTemplate redisTemplate;

	@Autowired
	private TbOrderItemMapper orderItemMapper;

	@Autowired
	private TbOrderMapper orderMapper;

	public void saveOrder(TbOrder order) {
		// 从Redis中查询购物车信息
		List<Cart> cartList = (List<Cart>) redisTemplate.boundHashOps("cartList").get(order.getUserId());
		for (Cart cart : cartList) {
			// 封装order数据
			TbOrder tbOrder = new TbOrder();
			// 使用雪花算法生成id
			long orderId = idWork.nextId();
			// 将原order中的数据移植到新的TBOrder对象中
			tbOrder.setPaymentType(order.getPaymentType());
			tbOrder.setReceiverAreaName(order.getReceiverAreaName());
			tbOrder.setReceiverMobile(order.getReceiverMobile());
			tbOrder.setSourceType(order.getSourceType());
			// 封装其他数据
			tbOrder.setOrderId(orderId);
			tbOrder.setStatus("1");
			tbOrder.setCreateTime(new Date());
			tbOrder.setUpdateTime(tbOrder.getCreateTime());
			tbOrder.setSellerId(cart.getSellerId());

			List<TbOrderItem> orderItems = cart.getOrderItems();
			// 定义订单总价格
			double payment = 0.00;

			for (TbOrderItem orderItem : orderItems) {
				orderItem.setId(idWork.nextId());
				orderItem.setOrderId(orderId);
				payment += orderItem.getTotalFee().doubleValue();
				orderItemMapper.insert(orderItem);
			}
			tbOrder.setPayment(new BigDecimal(payment));
			orderMapper.insert(tbOrder);
		}

		// 订单生成之后删除redis中的购物车数据
		redisTemplate.boundHashOps("cartList").delete(order.getUserId());

	}

}


四、二维码插件使用


1、二维码优势


        信息容量大, 可以容纳多达1850个大写字母或2710个数字或500多个汉字


        应用范围广, 支持文字,声音,图片,指纹等等...


        容错能力强, 即使图片出现部分破损也能使用


        成本低, 容易制作


2、二维码容错级别


        L级(低) 7%的码字可以被恢复。


        M级(中) 的码字的15%可以被恢复。


        Q级(四分)的码字的25%可以被恢复。


        H级(高) 的码字的30%可以被恢复。


3、二维码生成插件--qrious


qrious.js二维码插件的可用配置参数如下:




4、二维码生成demo


<html>
<head>
<title>二维码入门小demo</title>
</head>
<body>
<img id="qrious">
<script src="qrious.min.js"></script>
<script>
//js代码必须在<img>标签之下
 var qr = new QRious({
	   element:document.getElementById('qrious'),
	   size:250,
 	   level:'H',	
   value:'http://www.itcast.cn'
	});
</script>
</body>
</html>


五、微信支付流程简介


1、微信支付


开发文档:https://pay.weixin.qq.com/wiki/doc/api/index.html


六、支付系统准备工作


1、搭建支付系统maven module -- interface、service、web


interface -- jar


service -- war


web -- war


2、配置文件以及静态资源


(1)、配置文件及静态资源参考cart系统的配置




(2)、service的pom.xml中加入微信支付的jar包坐标


		<!-- 导入微信支付需要的jar包坐标 -->
		<dependency>
			<groupId>com.github.wxpay</groupId>
			<artifactId>wxpay-sdk</artifactId>
			<version>0.0.3</version>
		</dependency>


(3)、web项目中的applicationContext-security.xml


<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
	xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
						http://www.springframework.org/schema/beans/spring-beans.xsd
						http://www.springframework.org/schema/security 
						http://www.springframework.org/schema/security/spring-security.xsd">


	<!-- 设置不拦截页面 -->
	<http pattern="/register.html" security="none"></http>
	<http pattern="/assets/**" security="none"></http>
	<http pattern="/css/**" security="none"></http>
	<http pattern="/data/**" security="none"></http>
	<http pattern="/fonts/**" security="none"></http>
	<http pattern="/img/**" security="none"></http>
	<http pattern="/js/**" security="none"></http>
	<http pattern="/plugins/**" security="none"></http>
	<http pattern="/favicon.ico" security="none"></http>

	<!-- entry-point-ref 入口点引用 -->
	<http use-expressions="false" entry-point-ref="casProcessingFilterEntryPoint">
		<intercept-url pattern="/**" access="ROLE_USER" />
		<csrf disabled="true" />
		<!-- 
			custom-filter为过滤器, position 表示将过滤器放在指定的位置上,
			before表示放在指定位置之前 ,
			after表示放在指定的位置之后 
		-->
		<custom-filter ref="casAuthenticationFilter" position="CAS_FILTER" />
		<custom-filter ref="requestSingleLogoutFilter" before="LOGOUT_FILTER" />
		<custom-filter ref="singleLogoutFilter" before="CAS_FILTER" />
	</http>

	<!-- CAS入口点 开始 -->
	<beans:bean id="casProcessingFilterEntryPoint"
		class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">
		<!-- 单点登录服务器登录URL -->
		<beans:property name="loginUrl" value="http://cas.pinyougou.com/cas/login" />
		<beans:property name="serviceProperties" ref="serviceProperties" />
	</beans:bean>
	<beans:bean id="serviceProperties"
		class="org.springframework.security.cas.ServiceProperties">
		<!--service 配置自身工程的根地址+/login/cas -->
		<beans:property name="service" value="http://pay.pinyougou.com/login/cas" />
	</beans:bean>
	<!-- CAS入口点 结束 -->

	<!-- 认证过滤器 开始 -->
	<beans:bean id="casAuthenticationFilter" class="org.springframework.security.cas.web.CasAuthenticationFilter">
		<beans:property name="authenticationManager" ref="authenticationManager" />
	</beans:bean>
	<!-- 认证管理器 -->
	<authentication-manager alias="authenticationManager">
		<authentication-provider ref="casAuthenticationProvider">
		</authentication-provider>
	</authentication-manager>
	<!-- 认证提供者 -->
	<beans:bean id="casAuthenticationProvider"
		class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
		<beans:property name="authenticationUserDetailsService">
			<beans:bean class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
				<beans:constructor-arg ref="userDetailsService" />
			</beans:bean>
		</beans:property>
		<beans:property name="serviceProperties" ref="serviceProperties" />
		<!-- ticketValidator 为票据验证器 -->
		<beans:property name="ticketValidator">
			<beans:bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
				<beans:constructor-arg index="0" value="http://cas.pinyougou.com/cas" />
			</beans:bean>
		</beans:property>
		<beans:property name="key" value="an_id_for_this_auth_provider_only" />
	</beans:bean>
	<!-- 认证类 -->
	<beans:bean id="userDetailsService" class="com.pinyougou.user.service.UserDetailServiceImpl" />

	<!-- 认证过滤器 结束 -->
	<!-- 单点登出 开始 -->
	<beans:bean id="singleLogoutFilter" class="org.jasig.cas.client.session.SingleSignOutFilter" />
	<beans:bean id="requestSingleLogoutFilter"
		class="org.springframework.security.web.authentication.logout.LogoutFilter">
		<beans:constructor-arg value="http://cas.pinyougou.com/cas/logout?service=http://www.baidu.com" />
		<beans:constructor-arg>
			<beans:bean
				class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler" />
		</beans:constructor-arg>
		<beans:property name="filterProcessesUrl" value="/logout/cas" />
	</beans:bean>
	<!-- 单点登出 结束 -->
</beans:beans>


3、pinyougou-common中加入httpclient工具类并导入httpclient的jar包


package utils;

import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.text.ParseException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

/**
 * http请求客户端
 * 
 * @author Administrator
 * 
 */
public class HttpClient {
	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 HttpClient(String url, Map<String, String> param) {
		this.url = url;
		this.param = param;
	}

	public HttpClient(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);
			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;
	}

}


		<!-- 导入httpclient的jar包坐标 -->
		<dependency>
			<groupId>org.apache.httpcomponents</groupId>
			<artifactId>httpclient</artifactId>
		</dependency>


七、完成订单支付的功能


1、修改order系统中遗留问题


(1)、生成订单时的日志保存操作


说明:保存订单的时候,将支付日志信息中订单相关的数据也保存在数据库中




package com.pinyougou.order.service.impl;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.dubbo.config.annotation.Service;
import com.pinyougou.mapper.TbOrderItemMapper;
import com.pinyougou.mapper.TbOrderMapper;
import com.pinyougou.mapper.TbPayLogMapper;
import com.pinyougou.order.service.OrderService;
import com.pinyougou.pojo.TbOrder;
import com.pinyougou.pojo.TbOrderItem;
import com.pinyougou.pojo.TbPayLog;
import com.resultentity.Cart;

import utils.IdWorker;

@Service
@Transactional
public class OrderServiceImpl implements OrderService {
	@Autowired
	private IdWorker idWork;

	@Autowired
	private RedisTemplate redisTemplate;

	@Autowired
	private TbOrderItemMapper orderItemMapper;

	@Autowired
	private TbOrderMapper orderMapper;
	
	@Autowired
	private TbPayLogMapper payLogMapper;

	public void saveOrder(TbOrder order) {
		// 从Redis中查询购物车信息
		List<Cart> cartList = (List<Cart>) redisTemplate.boundHashOps("cartList").get(order.getUserId());

		// 用来拼接orderId,并放入log日志中
		String orderList = "";

		// 用于汇总所有订单的价格,为了放入log日志中
		double totalFee = 0.00;

		for (Cart cart : cartList) {
			// 封装order数据
			TbOrder tbOrder = new TbOrder();
			// 使用雪花算法生成id
			long orderId = idWork.nextId();

			// 将id放入orderList中,保存在支付日志中
			orderList += "," + orderId;

			// 将原order中的数据移植到新的TBOrder对象中
			tbOrder.setPaymentType(order.getPaymentType());
			tbOrder.setReceiverAreaName(order.getReceiverAreaName());
			tbOrder.setReceiverMobile(order.getReceiverMobile());
			tbOrder.setSourceType(order.getSourceType());
			// 封装其他数据
			tbOrder.setOrderId(orderId);
			tbOrder.setStatus("1");
			tbOrder.setCreateTime(new Date());
			tbOrder.setUpdateTime(tbOrder.getCreateTime());
			tbOrder.setSellerId(cart.getSellerId());

			List<TbOrderItem> orderItems = cart.getOrderItems();
			// 定义订单总价格
			double payment = 0.00;

			for (TbOrderItem orderItem : orderItems) {
				orderItem.setId(idWork.nextId());
				orderItem.setOrderId(orderId);
				payment += orderItem.getTotalFee().doubleValue();
				orderItemMapper.insert(orderItem);
				//将金额加入到支付日志信息的总价中
				totalFee += payment;
			}
			tbOrder.setPayment(new BigDecimal(payment));
			orderMapper.insert(tbOrder);
		}

		// 生成订单,在数据库中插入一条支付日志数据
		TbPayLog payLog = new TbPayLog();
		payLog.setCreateTime(new Date());
		payLog.setOrderList(orderList.substring(1));
		payLog.setOutTradeNo(idWork.nextId() + "");
		// payLog.setPayTime(payTime);
		payLog.setPayType(order.getPaymentType());
		payLog.setTotalFee((long) (totalFee * 100));
		payLog.setTradeState("0"); // 0未付款 1已付款
		// payLog.setTransactionId(transactionId);
		payLog.setUserId(order.getUserId());

		payLogMapper.insert(payLog);
		
		// 把支付日志信息放入redis缓存
		redisTemplate.boundHashOps("payLog").put(order.getUserId(), payLog);

		// 订单生成之后删除redis中的购物车数据
		redisTemplate.boundHashOps("cartList").delete(order.getUserId());

	}

}


(2)、订单生成之后跳转到支付页面操作




2、生成支付的二维码并进行支付操作


(1)、pay.html




(3)、payfaile.html




(3)、payController.js


//定义控制器
app.controller("payController", function($scope, $location, payService) {

	// 进入页面生成二维码的操作
	$scope.createNativeUrl = function() {
		payService.createNativeUrl().success(function(data) {
			$scope.resultMap = data;
			// 根据返回的url生成二维码
			var q = new QRious({
				value : data.code_url,
				element : document.getElementById("payImg"),
				size : 300,
				level : 'L'
			});

			// 二维码生成之后,轮询查看是否支付完成
			$scope.queryOrder(data.out_trade_no);
		});
	}

	// 轮询查询订单支付是否完成
	$scope.queryOrder = function(out_trade_no) {
		payService.queryOrder(out_trade_no).success(
				function(data) {
					if (data.success) {
						// 支付成功,跳转到成功页面,并携带支付金额
						location.href = "paysuccess.html#?totalFee="
								+ $scope.resultMap.total_fee;
					} else {
						if (data.message == "支付失败") {
							location.href = "payfail.html";
						} else if (data.message == "二维码失效") {
							// 重新生成支付的二维码
							$scope.createNativeUrl();
						}
					}
				});
	}

	// 获取支付总金额的方法
	$scope.getMoney = function() {
		// 获取其他页面请求参数
		return $location.search()['totalFee'];
	}
});


(4)、payService.js


/*用户的service层代码*/
app.service("payService", function($http) {

	// 商城二维码操作
	this.createNativeUrl = function() {
		return $http.get("./pay/createNativeUrl.do");
	}

	// 轮询支付状态操作
	this.queryOrder = function(out_trade_no) {
		return $http.get("./pay/queryOrder.do?out_trade_no=" + out_trade_no);
	}
});


(5)、PayController.java


package com.pinyougou.pay.controller;

import java.util.Map;

import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.alibaba.dubbo.config.annotation.Reference;
import com.pinyougou.pay.service.PayService;
import com.resultentity.ResultMessage;

@RestController
@RequestMapping("/pay")
public class PayController {
	
	@Reference
	private PayService payService;

	/**
	 * 生成二维吗
	 * @return
	 */
	@RequestMapping("/createNativeUrl")
	public Map<String, String> createNativeUrl() {
		//获取当前登录人信息
		String userId = SecurityContextHolder.getContext().getAuthentication().getName();
		Map<String, String> map = payService.createNativeUrl(userId);
		return map;
	}
	
	@RequestMapping("/queryOrder")
	public ResultMessage queryOrder(String out_trade_no) {
		//规定查询30分钟,30分钟之后,二维码失效
		int times = 1;
		while(true) {
			Map<String, String> resultMap = payService.queryOrder(out_trade_no);
			//判断支付状态
			if(resultMap == null) {
				return new ResultMessage(false, "支付失败");
			}else if(resultMap.get("trade_state").equals("SUCCESS")) {
				//支付成功,修改日志表
				String transaction_id = resultMap.get("transaction_id");
				payService.updatePayLog(out_trade_no, transaction_id);
				
				return new ResultMessage(true, "支付成功");
			}else if(resultMap.get("trade_state").equals("NOTPAY")) {
				//支付未完成,whileTrue继续重复查询操作
			}
			
			try {
				//3秒查询一次
				Thread.sleep(3000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			times ++;
			if(times > 600) {
				return new ResultMessage(false, "二维码失效");
			}
		}
	}
}


(6)、PayServiceImpl.java


package com.pinyougou.pay.service.impl;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;

import com.alibaba.dubbo.config.annotation.Service;
import com.github.wxpay.sdk.WXPayUtil;
import com.pinyougou.mapper.TbOrderMapper;
import com.pinyougou.mapper.TbPayLogMapper;
import com.pinyougou.pay.service.PayService;
import com.pinyougou.pojo.TbOrder;
import com.pinyougou.pojo.TbPayLog;

import utils.HttpClient;
import utils.IdWorker;

@Service
public class PayServiceImpl implements PayService {

	@Value("${httpClientUrl}")
	private String httpClientUrl;

	@Value("${appid}")
	private String appid;

	@Value("${partner}")
	private String partner;

	@Value("${partnerkey}")
	private String partnerkey;

	@Value("${notifyurl}")
	private String notifyurl;

	@Autowired
	private IdWorker idWorker;

	@Autowired
	private RedisTemplate redisTemplate;

	/**
	 * 生成二维码
	 */
	public Map<String, String> createNativeUrl(String userId) {

		try {

			// 从redis中获取用户支付日志信息
			TbPayLog payLog = (TbPayLog) redisTemplate.boundHashOps("payLog").get(userId);
			// 获取商户订单号
			String out_trade_no = payLog.getOutTradeNo();

			HttpClient httpClient = new HttpClient(httpClientUrl);
			Map<String, String> paramMap = new HashMap<String, String>();
			// 公众账号ID appid
			paramMap.put("appid", appid);
			// 商户号 mch_id
			paramMap.put("mch_id", partner);
			// 随机字符串 nonce_str
			paramMap.put("nonce_str", WXPayUtil.generateNonceStr());
			// 商品描述 body
			paramMap.put("body", "品优购支付");
			// 商户订单号 out_trade_no
			System.out.println("商户订单号	:" + out_trade_no);
			paramMap.put("out_trade_no", out_trade_no);
			// 标价金额 total_fee,单位是分,从payLog中获取
			// paramMap.put("total_fee", payLog.getTotalFee() + "");
			paramMap.put("total_fee", "1");
			// 终端IP spbill_create_ip
			paramMap.put("spbill_create_ip", "127.0.0.1");
			// 通知地址 notify_url
			paramMap.put("notify_url", notifyurl);
			// 交易类型 trade_type
			paramMap.put("trade_type", "NATIVE");

			// 生成xml字符串 顺便生成了签名 sign
			String xmlParam = WXPayUtil.generateSignedXml(paramMap, partnerkey);
			// 设置参数
			httpClient.setXmlParam(xmlParam);
			// 发送请求
			httpClient.post();
			// 获取返回值
			String content = httpClient.getContent();
			// xml转换为map
			Map<String, String> resultMap = WXPayUtil.xmlToMap(content);

			// 返回结果集中放入商户订单号
			resultMap.put("out_trade_no", out_trade_no + "");
			// 返回结果集中放入支付金额
			resultMap.put("total_fee", payLog.getTotalFee() + "");

			System.out.println("支付调用生成url的结果:" + resultMap);

			return resultMap;

		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * 查询订单支付状态
	 */
	public Map<String, String> queryOrder(String outTradeNo) {
		try {
			HttpClient httpClient = new HttpClient("https://api.mch.weixin.qq.com/pay/orderquery");
			Map<String, String> paramMap = new HashMap<String, String>();
			// 公众账号ID appid
			paramMap.put("appid", appid);
			// 商户号 mch_id
			paramMap.put("mch_id", partner);
			// 随机字符串 nonce_str
			paramMap.put("nonce_str", WXPayUtil.generateNonceStr());
			// 商户订单号 out_trade_no
			paramMap.put("out_trade_no", outTradeNo + "");

			// 生成xml字符串 顺便生成了签名 sign
			String xmlParam = WXPayUtil.generateSignedXml(paramMap, partnerkey);
			httpClient.setXmlParam(xmlParam);
			httpClient.post();
			String content = httpClient.getContent();
			Map<String, String> resultMap = WXPayUtil.xmlToMap(content);

			System.out.println("调用支付状态生成的结果:" + resultMap);

			return resultMap;

		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	@Autowired
	private TbPayLogMapper payLogMapper;

	@Autowired
	private TbOrderMapper orderMapper;

	/**
	 * 修改支付日志
	 */
	public void updatePayLog(String outTradeNo, String transaction_id) {

		// 更新payLog
		TbPayLog payLog = payLogMapper.selectByPrimaryKey(outTradeNo);
		payLog.setPayTime(new Date());
		payLog.setTradeState("1");
		payLog.setTransactionId(transaction_id);
		payLogMapper.updateByPrimaryKey(payLog);
		// 更新order
		String[] orderIds = payLog.getOrderList().split(",");
		for (String orderId : orderIds) {
			TbOrder tbOrder = orderMapper.selectByPrimaryKey(Long.parseLong(orderId));
			tbOrder.setPaymentTime(new Date());
			tbOrder.setStatus("2"); // 代表已付款
			tbOrder.setUpdateTime(new Date());
			orderMapper.updateByPrimaryKey(tbOrder);
		}

		// 把redis中此用户的支付日志清空
		redisTemplate.boundHashOps("payLog").delete(payLog.getUserId());
	}

}


猜你喜欢

转载自blog.csdn.net/wingzhezhe/article/details/80781396
今日推荐