微信支付介绍

微信支付介绍

微信扫码支付是商户系统按微信支付协议生成支付二维码,用户再用微信“扫一扫”完成支付的模式。该模式适用于PC网站支付、实体店单品或订单支付、媒体广告支付等场景。 微信支付开发者文档

微信扫码支付申请

第一步:注册公众号(类型须为:服务号)

请根据营业执照类型选择以下主体注册:个体工商户| 企业/公司| 政府| 媒体| 其他类型。

第二步:认证公众号

公众号认证后才可申请微信支付,认证费:300元/年。

第三步:提交资料申请微信支付

登录公众平台,点击左侧菜单【微信支付】,开始填写资料等待审核,审核时间为1-5个工作日内。

第四步:开户成功,登录商户平台进行验证

资料审核通过后,请登录联系人邮箱查收商户号和密码,并登录商户平台填
写财付通备付金打的小额资金数额,完成账户验证。

第五步:在线签署协议

本协议为线上电子协议,签署后方可进行交易及资金结算,签署完立即生效。

微信扫码支付流程

在这里插入图片描述1、用户在商户侧完成下单,使用微信支付进行支付

2、由商户后台向微信支付发起下单请求(调用统一下单接口)注:交易类型trade_type=MWEB

3、统一下单接口返回支付相关参数给商户后台,如支付跳转url(参数名“mweb_url”),商户通过mweb_url调起微信支付中间页

4、中间页进行H5权限的校验,安全性检查(此处常见错误请见下文)

5、如支付成功,商户后台会接收到微信侧的异步通知

6、用户在微信支付收银台完成支付或取消支付,返回商户页面(默认为返回支付发起页面)

7、商户在展示页面,引导用户主动发起支付结果的查询

8,9、商户后台判断是否接收到微信侧的支付结果通知,如没有,后台调用我们的订单查询接口确认订单状态

10、展示最终的订单支付结果给用户

微信扫码支付具体实现

1、引入微信支付的SDK

         <dependency>
            <groupId>com.github.wxpay</groupId>
            <artifactId>wxpay-sdk</artifactId>
            <version>0.0.3</version>
        </dependency>

2、添加配置依赖

:此处为什么要整合redis呢,就是让生成的二维码,在一定时间内扫码有效,有效时间过期后,无法扫码

#关联的公众号appid
weixin.appid=
#商户号
weixin.partner=
#商户key
weixin.partnerkey=

#redis配置
#ip
spring.redis.host=
#端口
spring.redis.port=6379
#使用redis第几个库
spring.redis.database=2
#超时时间
spring.redis.timeout=1800000


#lectuce配置
spring.redis.lettuce.pool.max-active=20
#最大阻塞等待时间,负数表示没有限制
spring.redis.lettuce.pool.max-wait=-1
spring.redis.lettuce.pool.max-idle=5
spring.redis.lettuce.pool.min-idle=0

3、引入工具类

1、此工具类,是为了初始化配置文件中,微信支付相关配置内容

@Component
public class ConstantPropertiesUtils implements InitializingBean {

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

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

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


    public static String APPID;
    public static String PARTNER;
    public static String PARTNERKEY;
  

    @Override
    public void afterPropertiesSet() throws Exception {
        APPID = appid;
        PARTNER = partner;
        PARTNERKEY = partnerkey;
    }
}

2、此工具类是为了进行Http请求的工具类

public class HttpClient {
	private String url;
	private Map<String, String> param;
	private int statusCode;
	private String content;
	private String xmlParam;
	private boolean isHttps;
	private boolean isCert = false;
	//证书密码 微信商户号(mch_id)
	private String certPassword;

	public boolean isHttps() {
		return isHttps;
	}

	public void setHttps(boolean isHttps) {
		this.isHttps = isHttps;
	}

	public boolean isCert() {
		return isCert;
	}

	public void setCert(boolean cert) {
		isCert = cert;
	}

	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 String getCertPassword() {
		return certPassword;
	}

	public void setCertPassword(String certPassword) {
		this.certPassword = certPassword;
	}

	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) {
				if(isCert) {
					//TODO 需要完善
					FileInputStream inputStream = new FileInputStream(new File(ConstantPropertiesUtils.CERT));
					KeyStore keystore = KeyStore.getInstance("PKCS12");
					char[] partnerId2charArray = certPassword.toCharArray();
					keystore.load(inputStream, partnerId2charArray);
					SSLContext sslContext = SSLContexts.custom().loadKeyMaterial(keystore, partnerId2charArray).build();
					SSLConnectionSocketFactory sslsf =
							new SSLConnectionSocketFactory(sslContext,
									new String[] { "TLSv1" },
									null,
									SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
					httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
				} else {
					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;
	}
}

4、生成支付二维码

public Map createNative(Long orderId) {
        try {
            //从redis获取数据
            Map payMap = (Map)redisTemplate.opsForValue().get(orderId.toString());
            if(payMap != null) {
                return payMap;
            }
            //1 根据orderId获取订单信息
            OrderInfo order = orderService.getById(orderId);
            //2 向支付记录表添加信息
            paymentService.savePaymentInfo(order, PaymentTypeEnum.WEIXIN.getStatus());
            //3设置参数,
            //把参数转换xml格式,使用商户key进行加密
            Map paramMap = new HashMap();
            paramMap.put("appid", ConstantPropertiesUtils.APPID);
            paramMap.put("mch_id", ConstantPropertiesUtils.PARTNER);
            paramMap.put("nonce_str", WXPayUtil.generateNonceStr());
            String body = order.getReserveDate() + "就诊"+ order.getDepname();
            paramMap.put("body", body);
            paramMap.put("out_trade_no", order.getOutTradeNo());
            //paramMap.put("total_fee", order.getAmount().multiply(new BigDecimal("100")).longValue()+"");
            paramMap.put("total_fee", "1"); //为了测试,统一写成这个值
            paramMap.put("spbill_create_ip", "127.0.0.1");
            paramMap.put("notify_url", "http://guli.shop/api/order/weixinPay/weixinNotify");
            paramMap.put("trade_type", "NATIVE");
            //4 调用微信生成二维码接口,httpclient调用
            HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/unifiedorder");
            //设置map参数
            client.setXmlParam(WXPayUtil.generateSignedXml(paramMap,ConstantPropertiesUtils.PARTNERKEY));
            client.setHttps(true);
            client.post();
            //5 返回相关数据
            String xml = client.getContent();
            //转换map集合
            Map<String, String> resultMap = WXPayUtil.xmlToMap(xml);
            System.out.println("resultMap:"+resultMap);
            //6 封装返回结果集
            Map map = new HashMap<>();
            map.put("orderId", orderId);
            map.put("totalFee", order.getAmount());
            map.put("resultCode", resultMap.get("result_code"));
            map.put("codeUrl", resultMap.get("code_url")); //二维码地址

            if(resultMap.get("result_code") != null) {
                redisTemplate.opsForValue().set(orderId.toString(),map,120, TimeUnit.MINUTES);
            }
            return map;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

5、查询支付状态

1、controller

 //查询支付状态
    @GetMapping("queryPayStatus/{orderId}")
    public Result queryPayStatus(@PathVariable Long orderId) {
        //调用微信接口实现支付状态查询
        Map<String,String> resultMap = weixinService.queryPayStatus(orderId);
        //判断
        if(resultMap == null) {
            return Result.fail().message("支付出错");
        }
        if("SUCCESS".equals(resultMap.get("trade_state"))) { //支付成功
            //更新订单状态
            String out_trade_no = resultMap.get("out_trade_no");//订单编码
            paymentService.paySuccess(out_trade_no,resultMap);
            return Result.ok().message("支付成功");
        }
        return Result.ok().message("支付中");
    }

2、service

public Map<String, String> queryPayStatus(Long orderId) {
		try {
			//1 根据orderId获取订单信息
			OrderInfo orderInfo = orderService.getById(orderId);

			//2 封装提交参数
			Map paramMap = new HashMap();
			paramMap.put("appid", ConstantPropertiesUtils.APPID);
			paramMap.put("mch_id", ConstantPropertiesUtils.PARTNER);
			/**订单交易号*/
			paramMap.put("out_trade_no", orderInfo.getOutTradeNo());
			paramMap.put("nonce_str", WXPayUtil.generateNonceStr());

			//3 设置请求内容
			HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/orderquery");
			client.setXmlParam(WXPayUtil.generateSignedXml(paramMap,ConstantPropertiesUtils.PARTNERKEY));
			client.setHttps(true);
			client.post();

			//4 得到微信接口返回数据
			String xml = client.getContent();
			Map<String, String> resultMap = WXPayUtil.xmlToMap(xml);
			System.out.println("支付状态resultMap:"+resultMap);
			//5 把接口数据返回
			return resultMap;
		}catch(Exception e) {
			return null;
		}
	}

6、退款功能

参考文档:微信退款
接口需要使用证书,详情参考文档并下载证书
在这里插入图片描述
此处将退款证书,保存在当前模块目录下
在这里插入图片描述

public Boolean refund(Long orderId) {
		try {
			//获取支付记录信息
			PaymentInfo paymentInfo = paymentService.getPaymentInfo(orderId, PaymentTypeEnum.WEIXIN.getStatus());
			//添加信息到退款记录表
			RefundInfo refundInfo = refundInfoService.saveRefundInfo(paymentInfo);
			//判断当前订单数据是否已经退款
			if(refundInfo.getRefundStatus().intValue() == RefundStatusEnum.REFUND.getStatus().intValue()) {
				return true;
			}
			//调用微信接口实现退款
			//封装需要参数
			Map<String,String> paramMap = new HashMap<>();
			paramMap.put("appid",ConstantPropertiesUtils.APPID);       //公众账号ID
			paramMap.put("mch_id",ConstantPropertiesUtils.PARTNER);   //商户编号
			paramMap.put("nonce_str",WXPayUtil.generateNonceStr());
			paramMap.put("transaction_id",paymentInfo.getTradeNo()); //微信订单号
			paramMap.put("out_trade_no",paymentInfo.getOutTradeNo()); //商户订单编号
			paramMap.put("out_refund_no","tk"+paymentInfo.getOutTradeNo()); //商户退款单号
//       paramMap.put("total_fee",paymentInfoQuery.getTotalAmount().multiply(new BigDecimal("100")).longValue()+"");
//       paramMap.put("refund_fee",paymentInfoQuery.getTotalAmount().multiply(new BigDecimal("100")).longValue()+"");
			paramMap.put("total_fee","1"); // 订单金额
			paramMap.put("refund_fee","1"); //退款金额
			String paramXml = WXPayUtil.generateSignedXml(paramMap,ConstantPropertiesUtils.PARTNERKEY);
			//设置调用接口内容
			HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/secapi/pay/refund");
			client.setXmlParam(paramXml);
			client.setHttps(true);
			//设置证书信息
			client.setCert(true);
			//TODO 此处括号中为读取后的证书内容
			client.setCertPassword(。。。。);
			client.post();

			//接收返回数据
			String xml = client.getContent();
			Map<String, String> resultMap = WXPayUtil.xmlToMap(xml);
			if (null != resultMap && WXPayConstants.SUCCESS.equalsIgnoreCase(resultMap.get("result_code"))) {
				refundInfo.setCallbackTime(new Date());
				refundInfo.setTradeNo(resultMap.get("refund_id"));
				refundInfo.setRefundStatus(RefundStatusEnum.REFUND.getStatus());
				refundInfo.setCallbackContent(JSONObject.toJSONString(resultMap));
				refundInfoService.updateById(refundInfo);
				return true;
			}
			return false;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
	
}
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shenlbang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值