在这里介绍的是使用SpringBoot 来完成对微信页面wap商家支付
前提:使用了springboot框架技术来整合,实现了微信授权拉去用户信息,
开通了微信认证的公众号以及申请了商家支付以及域名备案。
商家支付需要开通微信认证后的公众号才能申请,个人的话目前不能申请。
参考下方官方微信公众号支付开发文档,本次是用了wap网页支付。
https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_1
自己看了文档说实话还是有点蒙蒙的,还是有结合一些相关视频进行学习,并简单实现微信支付。
wap支付是指商户在微信客户端外的移动端网页展示商品或服务,用户在前述页面确认使用微信支付时,商户发起本服务呼起微信客户端进行支付。
主要用于触屏版的手机浏览器请求微信支付的场景。可以方便的从外部浏览器唤起微信支付。
一、在项目导入相关支付pom依赖
用的是在github上别人写的一个封装包,可能版本有点旧,有兴趣的同学可以去github搜best-pay。
二、上微信支付商家平台
下方为官方微信支付商家网页
https://pay.weixin.qq.com/index.php/core/home/login
2.1 登录微信支付书,并点击产品中心,选中支付产品,JSAPI支付,里面会有相关支付展示
2.2 开发配置
在使用JSAPI支付和Native支付需要配置开发需要的域名
在此需要点击添加,并注意右边的问号的提示
2.3 添加备好案的域名
添加的时候看下提示,可以选择http和https
举例你的项目是
www.xxxx.com/项目名/映射地址/
www.xxxx.com/demo/pay/
注意:配置上面的地址后,只要在pay地址的目录都可以。
例如 /pay/demo/ /pay/demo/demo2
2.4 商户信息
第一步:需要微信支付商户号和密钥
第二步:点击商户信息和API设置密码
第三步:设置商户的key密码并安装证书
按步骤申请证书下载和密钥设置
三、项目配置
由于项目是使用springboot整合三大框架,所以在yml下配置已知的微信公众号appid、密钥、商户账号、密钥、证书目录以及需要用到的回调地址等。
3.1 商户账户实体类
@Data
@Component
@ConfigurationProperties(prefix ="wechat")
public class WechatAccounConfig {
private StringmpAppId;
private StringmpAppSecret;
/**
* 商户号
*/
private StringmchId;
/**
* 商户密钥
*/
private StringmchKey;
/**
* 商户证书路径
*/
private StringkeyPath;
/**
* 微信支付异步通知地址
*
*/
private StringnotifyUrl;
}
3.2 支付环境配置实体类
@Component
public class WechatPayConfig {
@Autowired
private WechatAccounConfigaccounConfig;
@Bean
public BestPayServiceImpl bestPayService(){
BestPayServiceImpl bestPayService =new BestPayServiceImpl();
bestPayService.setWxPayH5Config(wxPayH5Config());
return bestPayService;
}
@Bean
public WxPayH5Config wxPayH5Config(){
WxPayH5Config wxPayH5Config =new WxPayH5Config();
wxPayH5Config.setAppId(accounConfig.getMpAppId());
wxPayH5Config.setAppSecret(accounConfig.getMpAppSecret());
wxPayH5Config.setMchId(accounConfig.getMchId());
wxPayH5Config.setMchKey(accounConfig.getMchKey());
wxPayH5Config.setKeyPath(accounConfig.getKeyPath());
wxPayH5Config.setNotifyUrl(accounConfig.getNotifyUrl());
return wxPayH5Config;
}
}
3.3 写一个支付接口的类并且实现
public interface PayService {
/**
* 创建支付订单
* @param orderDTO
*/
PayResponse create(OrderDTO orderDTO);
/**
* 异步通知
* @param notify
*/
PayResponse notify(String notify);
}
3.4 支付接口实现类
@Service("payService")
@Slf4j
public class PayServiceImplimplements PayService {
private static final StringORDER_NAME ="微信预约";
@Autowired
private BestPayServiceImplbestPayService;
@Autowired
private AppointOrderServiceorderService;
@Autowired
private AppointmentMapperappointmentMapper;
@Override
public PayResponse create(OrderDTO orderDTO) {
Appointment appointment =appointmentMapper.selectByPrimaryKey(orderDTO.getAppointId());
PayRequest payRequest =new PayRequest();
payRequest.setOpenid(orderDTO.getCustomerOpenid());
payRequest.setOrderAmount(orderDTO.getOrderAmount().doubleValue());
payRequest.setOrderId(orderDTO.getOrderId());
payRequest.setOrderName(ORDER_NAME);
payRequest.setPayTypeEnum(BestPayTypeEnum.WXPAY_H5);
log.info("[微信支付]发起支付,request={}", JsonUtil.toJson(payRequest));
PayResponse payResponse =bestPayService.pay(payRequest);
log.info("[微信支付]发起支付,response ={}",JsonUtil.toJson(payRequest));
return payResponse;
}
@Override
public PayResponse notify(String notify) {
//1.验证签名
//2.支付的状态
//3.支付金额
//4.支付人(下单人 == 支付人)
PayResponse payResponse =bestPayService.asyncNotify(notify);
log.info("[微信支付]异步通知,payResponse={}",JsonUtil.toJson(payResponse));
//查询订单
OrderDTO orderDTO =orderService.findOne(payResponse.getOrderId());
//判断订单是否存在
if (orderDTO ==null){
log.error("[微信支付]异步通知,订单不存在,orderId={}",payResponse.getOrderId());
throw new AppointException(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 AppointException(ResultEnum.WXPAY_NOTIFY_MONEY_ERROR);
}
//修改订单支付状态
orderService.paid(orderDTO);
return payResponse;
}
}
3.5 前端页面传值给下单接口,并返回数据给回调地址
在之前必须实现一个下单接口,并通过回调地址实现对支付的发起
wechatPayUrl: www.xxx.com/项目名/pay/create 的接口
returnUrl: 是你支付完后返回的地址
3.6 location.href 接口Controller代码实现以及微信异步通知
@RestController
@RequestMapping("/pay")
public class PayController {
@Autowired
private AppointOrderServiceorderService;
@Autowired
private PayServicepayService;
@GetMapping("/create")
public ModelAndView create(@RequestParam("orderId")String orderId,
@RequestParam("returnUrl")String returnUrl,
Map map){
//1.查询订单
OrderDTO orderDTO =orderService.findOne(orderId);
if (orderDTO ==null){
throw new AppointException(ResultEnum.ORDER_NOT_EXIST);
}
//2.发起支付
PayResponse payResponse =payService.create(orderDTO);
map.put("payResponse",payResponse);
map.put("returnUrl",returnUrl);
return new ModelAndView("pay/create",map);
}
/**
* 微信异步通知
* 需要配置
*/
@PostMapping("/notify")
public ModelAndView notify(@RequestBody String notifyData){
payService.notify(notifyData);
//返回给微信处理
return new ModelAndView("pay/success");
}
}
3.7 需要写js和xml调起微信支付窗口和返回结果
可以通过API列表了解一下
https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=9_20&index=1
项目是用freemarker
create.ftl
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,但并不保证它绝对可靠。
location.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();
}
success.ftl
四、实现支付功能
点击提交按钮,调用下单接口,前端用图7那样方式进行跳转发起支付接口
调起支付窗口并输入支付密码
注:本图为真机操作
注:该文章主要为个人学习内容