Java 从零开始实现微信支付(后台)

框架使用的是spring boot

我们集成微信支付 也就需要提供两个接口出来.一个是给前端 作用是拿到请求参数加签请求统一下单接口,得到预支付id,然后再加签返回给前端

前端拿到我们返回的数据.进行调起支付就行了,第二个接口是提供给微信支付的服务器,支付成功或者失败的时候.微信服务器会回调我们的这个接口.异步通知我们支付结果

好了.废话不哆嗦,直接上代码:


@RestController
@RequestMapping("/wxpayment")
public class WxPaymentController {
	public static final String WXUNIFIEDORDER_HTTPURL = "https://api.mch.weixin.qq.com/pay/unifiedorder";// 微信统一下单接口
	public static final String APP_ID = "";// 微信appid
	public static final String MCH_ID = "";// 商户号
	public static final String API_KEY = "";// apikey

	/**
	 * 对微信支付信息进行签名
	 * 
	 * @param info
	 *            数据类
	 * @return
	 * @throws AlipayApiException
	 * @throws UnsupportedEncodingException
	 */
	@PostMapping("/sign")
	public Object sign(@RequestBody WXSignInfo wxSignInfo, HttpServletRequest httpServletRequest)
			throws AlipayApiException, UnsupportedEncodingException {
		String create_ip = NetStateUtils.getIpAddr(httpServletRequest);
		ResponseWxSign4App response = new ResponseWxSign4App();
		response.setResult(wxUnifiedorder(create_ip, wxSignInfo.Content));
		String result = GsonUtils.object2Json(response);
		System.out.println("微信支付 加签结果:" + result);
		return result;
	}

	/**
	 * 微信支付成功后.微信服务器会回调该接口
	 * 
	 * @param request
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	@PostMapping("/notify")
	public String notify(HttpServletRequest request) throws UnsupportedEncodingException {
		System.out.println("notify come");
		Response2WxPayServer response2WxPayServer = new Response2WxPayServer();
		;
		// 读取 入参
		StringBuilder sb = new StringBuilder();
		try {
			BufferedReader br = new BufferedReader(
					new InputStreamReader((ServletInputStream) request.getInputStream()));
			String line = null;
			while ((line = br.readLine()) != null) {
				sb.append(line);
			}
			System.out.println("request:" + sb);
		} catch (Exception e) {
			e.printStackTrace();
			System.out.println("微信支付回调读取入参发生异常.直接返回更新失败");
			response2WxPayServer.setReturn_code(Constants.FAIL);
			response2WxPayServer.setReturn_msg("unknown");
			return XmlUtils.toXml(response2WxPayServer);
		}
		XStream xStream = new XStream();
		xStream.alias("xml", WxNotifyBean.class);
		WxNotifyBean wxNotifyBean = (WxNotifyBean) xStream.fromXML(sb.toString());
		System.out.println("notify info:" + GsonUtils.object2Json(wxNotifyBean));
		if (null != wxNotifyBean && wxNotifyBean.getReturn_code().equals(Constants.SUCCESS)
				&& wxNotifyBean.getResult_code().equals(Constants.SUCCESS)) {// 支付成功
			String result = PaymentController.updateALiPayOrderStatus(wxNotifyBean.getOut_trade_no());
			System.out.println("验证成功,去更新状态 \t订单号:" + wxNotifyBean.getOut_trade_no() + "来自微信支付,更新结果:" + result);
			BaseResponse baseResponse = GsonUtils.getGson().fromJson(result, BaseResponse.class);
			if (null != baseResponse && baseResponse.isSucceeded) {
				response2WxPayServer.setReturn_code(Constants.SUCCESS);
			} else {
				response2WxPayServer.setReturn_code(Constants.FAIL);
				response2WxPayServer.setReturn_msg("unknown");
			}
		} else {
			// 支付失败
			response2WxPayServer.setReturn_code(Constants.FAIL);
			response2WxPayServer.setReturn_msg("unknown");
		}
		return XmlUtils.toXml(response2WxPayServer);
	}

	// https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=4_3 文档地址
	/**
	 * 微信统一下单接口
	 * 
	 * @param orderNumber
	 * @return
	 */
	public Return4AppWxPayBean wxUnifiedorder(String create_ip, RequestUnifiedOrderBean requestUnifiedOrderBean) {
		requestUnifiedOrderBean.setNonce_str(NumUtils.getRandomStringByDate());
		requestUnifiedOrderBean.setSign("");
		requestUnifiedOrderBean.setSpbill_create_ip(create_ip);
		HashMap<String, String> hashMap = getHashMap2SignFromRequestUnifiedOrderBean(requestUnifiedOrderBean);
		String sign = addSign(hashMap);
		if (null == sign) {
			return null;
		}
		requestUnifiedOrderBean.setSign(sign);
		String requestContent = XmlUtils.getXstream().toXML(requestUnifiedOrderBean);
		requestContent = requestContent.replaceAll("__", "_");// xstream
																// 转换的时候会把单个下划线弄成两个,所以这里替换下
		String result = HttpUtils.doPost(WXUNIFIEDORDER_HTTPURL, requestContent);
		System.out.println("微信统一下单接口 返回结果:" + result);
		if (TextUtils.isEmpty(result)) {// 统一下单都失败了.直接返回null
			return null;
		}
		XStream xstream = XmlUtils.getXstream();
		xstream.alias("xml", ResponseUnifiedOrderBean.class);// xml对应ResponseUnifiedOrderBean的根节点的名字
		ResponseUnifiedOrderBean responseUnifiedOrderBean = (ResponseUnifiedOrderBean) xstream.fromXML(result);
		if (responseUnifiedOrderBean.getReturn_code().equals("SUCCESS")
				&& responseUnifiedOrderBean.getResult_code().equals("SUCCESS")) {// 全为成功.则进行相应的处理
			// 分割线
			Return4AppWxPayBean return4AppWxPayBean = new Return4AppWxPayBean();
			return4AppWxPayBean.setNonceStr(responseUnifiedOrderBean.getNonce_str());
			return4AppWxPayBean.setPrepayId(responseUnifiedOrderBean.getPrepay_id());
			// return4AppWxPayBean.setSign(responseUnifiedOrderBean.getSign());
			return4AppWxPayBean.setTimeStamp("" + TimeUtils.getTime_Second());
			// 再次签名appid,partnerid等全部参数必须全小写
			HashMap<String, String> hashMap2Sign = getHashMap2SignFromReturn4AppWxPayBean(return4AppWxPayBean);
			String sign1 = addSign(hashMap2Sign);
			if (null == sign1) {
				return null;
			}
			return4AppWxPayBean.setSign(sign1);
			return return4AppWxPayBean;
		} else {
			return null;
		}
	}

	/**
	 * 从requestUnifiedOrderBean 拿到一个待签名的hashmap
	 * 
	 * @param requestUnifiedOrderBean
	 * @return
	 */
	private HashMap<String, String> getHashMap2SignFromRequestUnifiedOrderBean(
			RequestUnifiedOrderBean requestUnifiedOrderBean) {
		HashMap<String, String> hashMap = new HashMap<>();
		hashMap.put("appid", requestUnifiedOrderBean.getAppid());
		hashMap.put("device_info", requestUnifiedOrderBean.getDevice_info());
		hashMap.put("mch_id", requestUnifiedOrderBean.getMch_id());
		hashMap.put("nonce_str", requestUnifiedOrderBean.getNonce_str());
		hashMap.put("notify_url", requestUnifiedOrderBean.getNotify_url());
		hashMap.put("spbill_create_ip", requestUnifiedOrderBean.getSpbill_create_ip());
		hashMap.put("trade_type", requestUnifiedOrderBean.getTrade_type());
		hashMap.put("body", requestUnifiedOrderBean.getBody());
		hashMap.put("total_fee", requestUnifiedOrderBean.getTotal_fee() + "");
		hashMap.put("out_trade_no", requestUnifiedOrderBean.getOut_trade_no());
		return hashMap;
	}

	/**
	 * 把信息从return4AppWxPayBean弄到hashmap里面
	 * 
	 * @param return4AppWxPayBean
	 * @return
	 */
	private HashMap<String, String> getHashMap2SignFromReturn4AppWxPayBean(Return4AppWxPayBean return4AppWxPayBean) {
		HashMap<String, String> hashMap2Sign = new HashMap<>();
		hashMap2Sign.put("appid", return4AppWxPayBean.getAppId());
		hashMap2Sign.put("partnerid", return4AppWxPayBean.getPartnerId());
		hashMap2Sign.put("prepayid", return4AppWxPayBean.getPrepayId());
		hashMap2Sign.put("noncestr", return4AppWxPayBean.getNonceStr());
		hashMap2Sign.put("timestamp", return4AppWxPayBean.getTimeStamp());
		hashMap2Sign.put("package", "Sign=WXPay");
		return hashMap2Sign;
	}

	/**
	 * 对hashmap进行加签
	 * 
	 * @param hashMap2Sign
	 * @return
	 */
	private String addSign(HashMap<String, String> hashMap2Sign) {
		String stringA2Sign = AlipayCore.createLinkString(hashMap2Sign);
		String stringSignTemp2Sign = stringA2Sign + "&key=" + API_KEY;
		String md5 = EncodeUtils.md5(stringSignTemp2Sign);
		if (null == md5) {// 签名失败 直接返回null
			return null;
		}
		String sign1 = md5.toUpperCase();
		return sign1;
	}
}

createLinkString方法是把一个hashmap依据参数按照参数名ASCII码从小到大排序(字典序),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string,方法实现如下:

 /** 
     * 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串
     * @param params 需要排序并参与字符拼接的参数组
     * @return 拼接后字符串
     */
    public static String createLinkString(Map<String, String> params) {

        List<String> keys = new ArrayList<String>(params.keySet());
        Collections.sort(keys);

        String prestr = "";

        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            String value = params.get(key);

            if (i == keys.size() - 1) {//拼接时,不包括最后一个&字符
                prestr = prestr + key + "=" + value;
            } else {
                prestr = prestr + key + "=" + value + "&";
            }
        }

        return prestr;
    }

说一下遇到的坑:

1.加签的时候appid和别的参数都是小写,并不是appId这种形式

2.本地服务器支付没问题.部署到win server2012服务器上时,签名失败,这个是因为本地服务器win8 默认utf-8编码,而win server 2012 默认 gbk编码,我们加签,进行md5编码的时候,把格式指定为utf-8就好了


  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值