小程序微信支付

小程序微信支付

前言:最近项目中用到小程序的支付和退款,趁热打铁写个博客,请大家参考,有问题可以私信我源码。

写之前先看一遍官方文档:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_7&index=3

以下是我项目中的代码:

1、wx.properties配置文件:主要用于配置支付所需的参数

#微信的配置参数
APPID=微信公众平台中
#开发者密码
APPSECRET=微信公众平台中
#微信商户号
MCH_ID=客户开通的商户号
#交易类型
TRADE_TYPE=JSAPI(公众号和小程序都能用次类型)
#微信支付回调地址
Pay_NOTIFY_URL=支付成功回调地址
#微信退款回调地址
#REFUND_NOTIFY_URL = 退款成功回调地址
#签名方式
SIGN_TYPE=MD5
#微信支付商户密钥
KEY=商户平台中下载证书的时候设置的密钥
#证书路径
SSLCERT_PATH = 证书路径
#默认密码 默认为商户号
SSLCERT_PASSWORD = 商户号

1.1 查看appId和AppSecret
在这里插入图片描述

1.2 配置支付地址和回调地址(公众号支付格式:域名+项目名称;扫码支付格式:域名+项目名称+回调地址所在controller地址)
在这里插入图片描述
1.3 设置密钥(随机生成32位密钥:https://suijimimashengcheng.51240.com/)
在这里插入图片描述

2、pom.xml文件中增加包

<dependency>
            <groupId>com.github.wxpay</groupId>
            <artifactId>wxpay-sdk</artifactId>
            <version>${com.github.wxpay.version}</version>
 </dependency>

3.、MyConfig方法

public class MyConfig implements WXPayConfig {
	private byte[] certData;

//	public MyConfig() throws Exception {
//		String certPath = MyConfig.class.getClassLoader().getResource("MP_verify_9VjkBIHQhQfxQ534.txt").getPath();
		URLDecoder decoder = new URLDecoder();
//		String path = URLDecoder.decode(certPath,"utf-8");
		File file = new File(certPath);
//		File file = new File(path);
//		InputStream certStream = new FileInputStream(file);
//		this.certData = new byte[(int) file.length()];
//		certStream.read(this.certData);
//		certStream.close();
//	}
	// APPID
	@Override
	public String getAppID() {
		return WXAuthUtil.APPID;
	}
	// 商户ID
	@Override
	public String getMchID() {
		return WXAuthUtil.MCH_ID;
	}
	// 获取接口秘钥
	@Override
	public String getKey() {
		return WXAuthUtil.KEY;
	}
	// 获取商户证书内容
	@Override
	public InputStream getCertStream() {
		ByteArrayInputStream certBis = new ByteArrayInputStream(this.certData);
		return certBis;
	}
	@Override
	public int getHttpConnectTimeoutMs() {
		return 8000;
	}
	@Override
	public int getHttpReadTimeoutMs() {
		return 10000;
	}

4、WXAuthUtil微信工具类

@SuppressWarnings("deprecation")
public class WXAuthUtil {
	static {
		APPID = PropertyUtil.getProperty("APPID");
		MCH_ID = PropertyUtil.getProperty("MCH_ID");
		KEY = PropertyUtil.getProperty("KEY");
		APPSECRET = PropertyUtil.getProperty("APPSECRET");
		TRADE_TYPE=PropertyUtil.getProperty("TRADE_TYPE");
		Pay_NOTIFY_URL=PropertyUtil.getProperty("Pay_NOTIFY_URL");
		SIGN_TYPE=PropertyUtil.getProperty("SIGN_TYPE");
		REFUND_NOTIFY_URL = PropertyUtil.getProperty("REFUND_NOTIFY_URL");
		SSLCERT_PATH = PropertyUtil.getProperty("SSLCERT_PATH");
		SSLCERT_PASSWORD = PropertyUtil.getProperty("SSLCERT_PASSWORD");
	}
	public static final String APPID;
	public static final String MCH_ID;
	public static final String KEY;
	public static final String APPSECRET;
	public static final String TRADE_TYPE;
	public static final String Pay_NOTIFY_URL;
	public static final String SIGN_TYPE;
	public static final String REFUND_NOTIFY_URL;
	public static final String SSLCERT_PATH;
	public static final String SSLCERT_PASSWORD;

	/**
	 * 通过url获取返回json对象
	 * 
	 * @param url
	 * @return
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
	public static JSONObject doGetJson(String url) throws ClientProtocolException, IOException {
		JSONObject jsonObject = null;
		DefaultHttpClient client = new DefaultHttpClient();
		HttpGet httpGet = new HttpGet(url);
		HttpResponse response = client.execute(httpGet);
		HttpEntity entity = response.getEntity();
		if (entity != null) {
			// 把返回的结果转换为JSON对象
			String result = EntityUtils.toString(entity, "UTF-8");
			jsonObject = JSON.parseObject(result);
		}
		client.close();
		return jsonObject;
	}
	public static JSONObject doPostJson(String url,String jsonString) throws ClientProtocolException, IOException {
		JSONObject jsonObject = null;
		DefaultHttpClient client = new DefaultHttpClient();
		HttpPost httpPost = new HttpPost(url);
		StringEntity stringEntity = new StringEntity(jsonString,"UTF-8");
		httpPost.setEntity(stringEntity);
		HttpResponse response = client.execute(httpPost);

		HttpEntity entity = response.getEntity();
		if (entity != null) {
			// 把返回的结果转换为JSON对象
			String result = EntityUtils.toString(entity, "UTF-8");
			jsonObject = JSON.parseObject(result);
		}
		client.close();
		return jsonObject;
	}
	/**
	 *  获得微信 AccessToken
	 * access_token是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token。
	 * 开发者需要access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取
	 * 的access_token失效。
	 * (此处我是把token存在Redis里面了)
	 */

	public static AccessToken getWxToken() throws IOException {
		JSONObject jsonObject =new JSONObject() ;

		String tokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+ APPID+"&secret="+ APPSECRET;
		DefaultHttpClient client = new DefaultHttpClient();
		HttpGet httpGet = new HttpGet(tokenUrl);
		HttpResponse response = client.execute(httpGet);
		HttpEntity entity = response.getEntity();
		if (entity != null) {
			// 把返回的结果转换为JSON对象
			String result = EntityUtils.toString(entity, "UTF-8");
			jsonObject = JSON.parseObject(result);
		}
		client.close();
		AccessToken access_token = new AccessToken();
		if (null != jsonObject) {
			try {
				access_token.setAccessToken(jsonObject.getString("access_token"));
				access_token.setExpiresin(jsonObject.getInteger("expires_in"));
			} catch (JSONException e) {
				access_token = null;
				// 获取token失败
				e.printStackTrace();
			}
		}
		return access_token;
	}

	public static void main(String[] args) throws IOException {
		AccessToken wxToken = getWxToken();
		System.out.println(wxToken);
	}
	 /*
     * 将SortedMap<Object,Object> 集合转化成 xml格式
     */
    public static String getRequestXml(SortedMap<Object,Object> parameters){
        StringBuffer sb = new StringBuffer();
        sb.append("<xml>");
        Set es = parameters.entrySet();
        Iterator it = es.iterator();
        while(it.hasNext()) {
            Map.Entry entry = (Map.Entry)it.next();
            String k = (String)entry.getKey();
            String v = (String)entry.getValue();
            if ("attach".equalsIgnoreCase(k)||"body".equalsIgnoreCase(k)||"sign".equalsIgnoreCase(k)) {
                sb.append("<"+k+">"+"<![CDATA["+v+"]]></"+k+">");
            }else {
                sb.append("<"+k+">"+v+"</"+k+">");
            }
        }
        sb.append("</xml>");
        return sb.toString();
    }

5、PropertyUtil 用来读取properties中的配置参数

/**
 * 读取wx.properties中的配置参数
 * @author zhaobaolong
 *
 */
public class PropertyUtil {
	private static final Logger logger = LoggerFactory.getLogger(PropertyUtil.class);
	private static Properties props;
	static {
		loadProps();
	}

	synchronized static private void loadProps() {
		logger.info("开始加载properties文件内容.......");
		props = new Properties();
		InputStream in = null;
		try {
			// 第一种,通过类加载器进行获取properties文件流
			in = PropertyUtil.class.getClassLoader().getResourceAsStream("wx.properties");
			props.load(in);
		} catch (FileNotFoundException e) {
			logger.error("wx.properties文件未找到");
		} catch (IOException e) {
			logger.error("出现IOException");
		} finally {
			try {
				if (null != in) {
					in.close();
				}
			} catch (IOException e) {
				logger.error("jdbc.properties文件流关闭出现异常");
			}
		}
		logger.info("加载properties文件内容完成...........");
		logger.info("properties文件内容:" + props);
	}

	public static String getProperty(String key) {
		if (null == props) {
			loadProps();
		}
		return props.getProperty(key);
	}

	public static String getProperty(String key, String defaultValue) {
		if (null == props) {
			loadProps();
		}
		return props.getProperty(key, defaultValue);
	}
}

5.1 其他所需要的类都在weixin-sdk的包中
在这里插入图片描述

接下来是支付控制层的代码:

/**
 * 微信支付
 *
 */
@Controller
@RequestMapping("/wx/app")
public class WXPayController {
	
	/**
	 * 统一下单
	 * @param total_fee 购买金额
	 * @param model
	 * @param openId 支付人的openId
	 * @return
	 * @throws Exception
	 */
	@RequestMapping("/orderPay")
	@ResponseBody
	public String unifiedOrder(Double total_fee,Model model,String openId) throws Exception {
		String spbill_create_ip = IpUtil.getIp(request);
		String orderNo = randomNo(orderInfoObjService.getOrderNum());//自动生成订单
		OrderPayObj payList = new OrderPayObj();
		// 统一下单
		MyConfig config = new MyConfig();
		WXPay wxpay = new WXPay(config);
		Map<String, String> data = new HashMap<String, String>();
		// 商品描述
		data.put("body", "商品描述");
		// 商户订单号
		data.put("out_trade_no", orderNo);
		int fee = (int) (total_fee * 100);
		// 标价金额 付款金额
		data.put("total_fee", String.valueOf(fee));
		// 客户终端IP
		data.put("spbill_create_ip", spbill_create_ip);
		// 异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。
		data.put("notify_url", WXAuthUtil.Pay_NOTIFY_URL);
		// 交易类型 公众号支付 
		data.put("trade_type", "JSAPI");
		// 用户标识
		data.put("openid", openId);
		Map<String, String> resp = null;
		try {
			resp = wxpay.unifiedOrder(data);
			for (Entry<String, String> entry : resp.entrySet()) {
				System.out.println("key:" + entry.getKey() + " Value:" + entry.getValue());
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		if (resp.get("return_code").equals("SUCCESS")) {
			if (resp.get("result_code").equals("SUCCESS")) {
				Map<String, String> Param = new HashMap<String, String>();
				Param.put("appId", WXAuthUtil.APPID);
				Param.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000));
				Param.put("nonceStr", WXPayUtil.generateNonceStr());
				Param.put("package", "prepay_id=" + resp.get("prepay_id"));
				Param.put("signType", WXAuthUtil.SIGN_TYPE);
				String sign = WXPayUtil.generateSignature(Param, config.getKey(), WXPayConstants.SignType.MD5);
				Param.put("paySign", sign);//验证签名
				model.addAttribute("appId", Param.get("appId"));
				model.addAttribute("timeStamp", Param.get("timeStamp"));
				model.addAttribute("nonceStr", Param.get("nonceStr"));
				model.addAttribute("pa", Param.get("package"));
				model.addAttribute("signType", Param.get("signType"));
				model.addAttribute("prepayId", resp.get("prepay_id"));
				model.addAttribute("paySign", sign);
				model.addAttribute("orderNo", orderNo);
				//次数可写生成支付订单,支付状态为未支付
				
			} else {
//				if(resp.get("err_code")) //判断返回值的类型,打印出对应的错误返回到前端
				payList.setCode("0");
				payList.setCodeMsg(resp.get("err_code"));
			}
		} else {
			// 通信失败,请检查网络
			payList.setCode("-1");
			payList.setCodeMsg(resp.get("return_msg"));
		}
		return JsonUtils.getJsonString(payList);
	}
	/**
	 * 自动生成订单
	 */
	public static String randomNo(int l) {
		String str = new SimpleDateFormat("yyyyMMddhhmmss")
				.format(new java.util.Date());
		long m = Long.parseLong((str)) * 10000;
		String ret = m +"_QL"+ l;
		return ret;
	}
	/**
	 * @Description : 支付结果通知
	 * 异步通知
	 */
	@RequestMapping("/notify_url")
	public void payNotify(HttpServletRequest request, HttpServletResponse response) {
		System.out.println("支付进入回调函数");
		Map<String, String> map = new HashMap<String, String>();
		String out_trade_no = null;
		String return_code = null;
		String return_msg = "";
		BigDecimal total_fee = null;
		try {
			InputStream inStream = request.getInputStream();
			ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
			byte[] buffer = new byte[1024];
			int len = 0;
			while ((len = inStream.read(buffer)) != -1) {
				outSteam.write(buffer, 0, len);
			}
			outSteam.close();
			inStream.close();
			String resultStr = new String(outSteam.toByteArray(), "utf-8");
			System.out.print("支付成功的回调:" + resultStr);
			Map<String, String> resultMap = WXPayUtil.xmlToMap(resultStr);
			out_trade_no = (String) resultMap.get("out_trade_no");
			return_code = (String) resultMap.get("return_code");
			total_fee = new BigDecimal(resultMap.get("total_fee"));
			request.setAttribute("out_trade_no", out_trade_no);
			response.setContentType("text/xml");
			if (return_code.equals("SUCCESS")) {
				// 通知微信.异步确认成功了.
				return_code = "SUCCESS";
				return_msg = "OK";
				// 此处写支付成功的业务逻辑
				
			} else {
				// 此处写支付失败的业务逻辑
				return_code = "FAIL";
			    return_msg = "error";
			}
			String mapToXml = WXPayUtil.mapToXml(map);
			response.getWriter().write(mapToXml);
			response.getWriter().flush();
		} catch (Exception e) {
			logger.debug("微信回调接口出现错误");
			System.out.print("微信回调接口出现错误 :" + e.getMessage());
			try {
				map.put("return_code", return_code);
				map.put("return_msg", return_msg);
				response.getWriter().write(WXPayUtil.mapToXml(map));
			} catch (Exception e1) {
				logger.debug("支付回调解析错误");
			}
		}
	}

	/**
	 * @description: 订单查询
	 * @date: 13:38 2018/9/17
	 * @param: orderNo
	 * @retrun:
	 */
	@RequestMapping("/orderPayQuery.do")
	@ResponseBody
	public String orderQuery(String orderNo) throws Exception {
		MyConfig config = new MyConfig();
		WXPay wxpay = new WXPay(config);
		Map<String, String> data = new HashMap<>();
		data.put("out_trade_no", orderNo);
		try {
			Map<String, String> resp = wxpay.orderQuery(data);
			System.out.println(resp);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return data.toString();
	}
}

前端调用(将后台这些数据返回到前端,前端调用)

timeStamp String 是 时间戳从1970年1月1日00:00:00至今的秒数,即当前的时间
nonceStr String 是 随机字符串,长度为32个字符以下。
package String 是 统一下单接口返回的 prepay_id 参数值,提交格式如:prepay_id=*
signType String 是 签名类型,默认为MD5,支持HMAC-SHA256和MD5。注意此处需与统一下单的签名类型一致
paySign String 是 签名,具体签名方案参见微信公众号支付帮助文档;
在这里插入图片描述

大概就是这些,可能个别有些漏掉,有不明白的私信我

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值