微信支付:
官方文档:https://pay.weixin.qq.com/wiki/doc/api/index.html
第三方SDK:https://github.com/Pay-Group/best-pay-sdk
首先说明一下,微信支付功能接口权限只有微信服务号才拥有。
首先来看一下公众号支付的业务流程图。
支付开发中,一般在第10步异步通知成功了,就基本上可以百分百说明支付成功了,可以修改支付状态为已支付。
开发步骤:
1.pom.xml中添加Maven依赖
<!-- 微信公众号支付依赖 -->
<dependency>
<groupId>cn.springboot</groupId>
<artifactId>best-pay-sdk</artifactId>
<version>1.1.0</version>
</dependency>
2.微信服务号信息配置
package com.wechat.order.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/*微信公众号账号设置*/
@Data//使用了lombok依赖,相当于在代码中getter和setter的作用
@Component
@ConfigurationProperties(prefix = "wechat")//账号信息写在了application.yml中。
public class WeChatAccountConfig {
private String mpAppId;
private String mpAppSecret;
/**
* 商户号
*/
private String mchId;
/**
* 商户密钥
*/
private String mchKey;
/**
* 商户证书路径
*/
private String keyPath;
/**
* 异步通知路径
*/
private String notifyUrl;
}
package com.wechat.order.config;
import com.lly835.bestpay.config.WxPayH5Config;
import com.lly835.bestpay.service.impl.BestPayServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
/*微信公众账号支付配置*/
@Component
public class WeChatPayConfig {
@Autowired
private WeChatAccountConfig weChatAccountConfig;
//把你要实例化的对象转化成一个Bean,放在IoC容器中
@Bean
public BestPayServiceImpl bestPayService() {
//支付类, 所有方法都在这个类里
BestPayServiceImpl bestPayService = new BestPayServiceImpl();
bestPayService.setWxPayH5Config(wxPayH5Config());
return bestPayService;
}
@Bean
private WxPayH5Config wxPayH5Config() {
WxPayH5Config wxPayH5Config = new WxPayH5Config();
wxPayH5Config.setAppId(weChatAccountConfig.getMpAppId());//设置微信公众号的appid
wxPayH5Config.setAppSecret(weChatAccountConfig.getMpAppSecret());// 设置微信公众号的app corpSecret
wxPayH5Config.setMchId(weChatAccountConfig.getMchId());// 设置商户号
wxPayH5Config.setMchKey(weChatAccountConfig.getMchKey());// 设置商户密钥
wxPayH5Config.setKeyPath(weChatAccountConfig.getKeyPath());// 设置商户证书路径
wxPayH5Config.setNotifyUrl(weChatAccountConfig.getNotifyUrl());// 设置支付后异步通知url
return wxPayH5Config;
}
}
3.调用方法发起支付
controller
/**
* 微信支付发起
* @param orderId
* @param returnUrl
* @return
*/
@RequestMapping("/create")
public ModelAndView create(@RequestParam("orderId")String orderId,
@RequestParam("returnUrl")String returnUrl,
HashMap<String, Object> map){
//根据订单id查询订单,数据库查询
OrderDTO orderDTO = orderService.findOne(orderId);
if(orderDTO == null){//订单不存在抛出错误
throw new SellException(ResultEnum.ORDER_NOT_EXIST);
}
//微信支付
PayResponse payResponse = wechatPayService.create(orderDTO);
//携带参数传入网页发起支付的前端模板
map.put("orderId", orderId);
map.put("payResponse", payResponse);
return new ModelAndView("pay/create",map);
}
service:
//微信支付发起
public PayResponse create(OrderDTO orderDTO) {
PayRequest payRequest = new PayRequest();
payRequest.setPayTypeEnum(BestPayTypeEnum.WXPAY_H5);//支付方式,微信公众账号支付
payRequest.setOrderId(orderDTO.getOrderId());//订单号.
payRequest.setOrderName(payName);//订单名字.
payRequest.setOrderAmount(orderDTO.getOrderAmount().doubleValue());//订单金额.
payRequest.setOpenid(orderDTO.getBuyerOpenid());//微信openid, 仅微信支付时需要
log.info("【微信支付】request={}", JsonUtil.toJson(payRequest));//将payRequest格式化一下,再显示在日志上,便于观看数据
PayResponse payResponse = bestPayService.pay(payRequest);
log.info("【微信支付】response={}", JsonUtil.toJson(payResponse));//将payResponse格式化一下,再显示在日志上,便于观看响应后返回的数据);
return payResponse;
}
JsonUtil格式转换工具类:
/**
* 把对象转换成json格式,便于观看
*/
public class JsonUtil {
public static String toJson(Object object) {
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setPrettyPrinting();
Gson gson = gsonBuilder.create();
return gson.toJson(object);
}
}
4.在static文件夹下编写微信内H5调起支付的前端代码pay.html(可以从微信支付官方文档中拷贝代码),检测是否可以支付, 此步骤可省略。
ps:为什么把支付的前端代码写在后台中?
如果放在前端的话,加入几个地方或几个模块都可以发起微信支付,每次发起都要调用这个前端代码,
这个前端代码就要写好几次。如果放在后端的话,只要给个请求,就可以直接调用这段代码。
5.对支付的前端代码动态注入参数以发起支付。
这里我们用到模板技术。
(1)在pom.xml中引入freemarker依赖
<!-- freemarker模板技术 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
(2)把controller中发起支付的方法返回类型改为ModelAndView,return new ModelAndView("path",model);//path为模板存放的路径,不包括模板后缀名。这里model我们采用Map类型,把payResponse和returnUrl携带过去。
(3)在相应的地方,创建模板文件xx.ftl。如我上述步骤的path为pay/create,则我在resources/templates目录下创建文件夹pay,在pay目录下再创建create.ftl文件。
(4)修改pay.html成模板,参数动态注入。${xxxx}
(5)模板中,支付后进行跳转到returnUrl。
create.ftl模板:
<script>
function onBridgeReady(){
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId":"${payResponse.appId}", //公众号名称,由商户传入
"timeStamp":"${payResponse.timeStamp}", //时间戳,自1970年以来的秒数
"nonceStr":"${payResponse.nonceStr}", //随机串
"package":"${payResponse.packAge}",
"signType":"MD5", //微信签名方式:
"paySign":"${payResponse.paySign}" //微信签名
},
function(res){
if(res.err_msg == "get_brand_wcpay_request:ok" ) {} // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。
}
local.href="${returnUrl}";
);
}
if (typeof WeixinJSBridge == "undefined"){
if( document.addEventListener ){
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
}else if (document.attachEvent){
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
}else{
onBridgeReady();
}
</script>
6.填写微信支付的支付授权目录。
比如我的前端支付代码访问路径是http://xxxxx(网站根路径)/sell/pay.html。
支付授权目录就写http://xxxxx(网站根路径)/sell/
7.微信异步通知,并在相应的controller中进行支付状态修改。
依靠前端返回的支付成功来判断成功并不安全,容易被篡改。必须依靠后端异步通知来判断是否已支付成功
(1)验证签名,支付状态
(2)校验支付金额与订单金额是否一致,2个金额相减小于0.01即认为相等。
(3)修改订单支付状态
service:
/*微信支付异步通知*/
@Override
public PayResponse notify(String notifyData) {
//1. 验证签名
//2. 支付的状态
//3. 支付金额
//4. 支付人(下单人 == 支付人)
PayResponse payResponse = bestPayService.asyncNotify(notifyData);
log.info("【微信支付】异步通知, payResponse={}", JsonUtil.toJson(payResponse));
//查询订单
OrderDTO orderDTO = orderService.findOne(payResponse.getOrderId());
//判断订单是否存在
if (orderDTO == null) {
log.error("【微信支付】异步通知, 订单不存在, orderId={}", payResponse.getOrderId());
throw new SellException(ResultEnum.ORDER_NOT_EXIST);
}
//判断金额是否一致(0.10 0.1)
if (!MathUtil.equals(payResponse.getOrderAmount(), orderDTO.getOrderAmount().doubleValue())) {
log.error("【微信支付】异步通知, 订单金额不一致, orderId={}, 微信通知金额={}, 系统金额={}",
payResponse.getOrderId(),
payResponse.getOrderAmount(),
orderDTO.getOrderAmount());
throw new SellException(ResultEnum.WXPAY_NOTIFY_MONEY_VERIFY_ERROR);
}
//修改订单的支付状态
orderService.paid(orderDTO);
return payResponse;
}
controller:
/**
* 微信异步通知
* @param notifyData
*/
@PostMapping("/notify")
public ModelAndView notify(@RequestBody String notifyData) {
wechatPayService.notify(notifyData);
//返回给微信处理结果
return new ModelAndView("pay/success");
}
}
success.ftl异步通知成功模板:
<xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
</xml>
8.微信退款
(1)设置退款人,退款金额,退款方式
(2)退款
service:
/**
* 退款
* @param orderDTO
*/
@Override
public RefundResponse refund(OrderDTO orderDTO) {
RefundRequest refundRequest = new RefundRequest();
refundRequest.setOrderId(orderDTO.getOrderId());
refundRequest.setOrderAmount(orderDTO.getOrderAmount().doubleValue());
refundRequest.setPayTypeEnum(BestPayTypeEnum.WXPAY_H5);
log.info("【微信退款】request={}", JsonUtil.toJson(refundRequest));
RefundResponse refundResponse = bestPayService.refund(refundRequest);
log.info("【微信退款】response={}", JsonUtil.toJson(refundResponse));
return refundResponse;
}
}
————————————————
版权声明:本文为CSDN博主「xsg6509」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/xsg6509/article/details/80342744