生成支付链接(用户点提交订单后)
用户点了提交订单后该给他显示微信支付二维码了。
生成步骤
1 controller接收的是订单编号
/**
* 创建支付链接
*
* @param orderId
* @return
*/
@GetMapping("/url/{id}")
public ResponseEntity<String> createPayUrl(@PathVariable("id") Long orderId) {
return ResponseEntity.ok(orderService.createPayUrl(orderId));
}
2 在service中处理:
这里面填的是上一篇PayHelper类里需要的三个参数
1,商品描述信息
2,商品订单编号
3,总金额
/**
* 创建支付链接
*
* @param orderId
* @return
*/
public String createPayUrl(Long orderId) {
// 查询订单
Order order = queryOrderById(orderId);
// 判断订单状态
Integer status = order.getOrderStatus().getStatus();
if (status != OrderStatusEnum.UNPAY.value()) {
throw new LyException(ExceptionEnum.ORDER_STATUS_EXCEPTION);
}
// 获取金额,单位是分,所以为了少给钱给一分吧
@NotNull Long totalPay = /*order.getActualPay()*/1l;
// 商品描述
OrderDetail detail = order.getOrderDetails().get(0);
String desc = detail.getTitle();
return payHelper.getPayUrl(desc, orderId, totalPay);
}
3 这样通过PayHelper处理后,前台页面就会出现一条支付链接:
前端用 js 二维码生成工具处理后就会有二维码了:
到这里支付链接就生成了。
然后就掏钱:
回调确认(用户支付成功后)
上一篇讲到,支付成功后微信会给我们发送一条消息,我们需要接受并返回消息。
问题:
我们是在本机进行测试,路径是本地,但是微信发送的消息是通过网络地址通知的,我们要接受就需要把自己电脑当做一台云服务器,要有一个网络地址,这就需要内网穿透来实现了。
1,内网穿透
简单地说就是给自己电脑一个网络ip,别人通过网络可以访问我们。
这里我使用一个工具来实现。
地址: https://natapp.cn
为什么使用它呢,免费啊。
注册创建用户然后选择免费隧道就好,傻瓜式操作。
下载符合自己电脑的工具,下载后是这样的:
有一个config.ini是自己创建的,官网也有模板,我贴出来自己的:
只有一个需要填:authtoken
填写自己的号就好,申请完隧道就会看见自己的authtoken。
#将本文件放置于natapp同级目录 程序将读取 [default] 段
#在命令行参数模式如 natapp -authtoken=xxx 等相同参数将会覆盖掉此配置
#命令行参数 -config= 可以指定任意config.ini文件
[default]
authtoken=xxxxxxxxxxxx #对应一条隧道的authtoken
clienttoken= #对应客户端的clienttoken,将会忽略authtoken,若无请留空,
log=none #log 日志文件,可指定本地文件, none=不做记录,stdout=直接屏幕输出 ,默认为none
loglevel=INFO #日志等级 DEBUG, INFO, WARNING, ERROR 默认为 DEBUG
http_proxy= #代理设置 如 http://10.123.10.10:3128 非代理上网用户请务必留空
然后点击exe运行就好,就像这样。
注意:地址我填的是8089,是我订单微服务的地址,因为是在这里个微服务里面处理回调确认的。
因为是免费的,所以那个地址会变化,实验一下就好,免费的要啥自行车呢。
2,业务实现
解决了地址问题,该返回消息了。
2.1 controller:
因为微信要的回值只支持xml,所以要返回xml类型的信息。
注意:
需要引入xml的配置,在pom文件中已经引入
有了xml的配置,才能让@requestbody注解解析xml,否则他默认只支持json
@Slf4j
@RestController
@RequestMapping("notify")
public class NotifyController {
@Autowired
private OrderService orderService;
/**
* produce:设置返回的一定是xml格式
* 微信支付成功回调
*
* @param msg
* @return
*/
@PostMapping(value = "pay", produces = "application/xml")
public ResponseEntity<String> payNotify(@RequestBody Map<String, String> msg) {
//处理回调结果
orderService.handleNotify(msg);
log.info("[订单回调],接收微信支付回调,结果:{}", msg);
// 没有异常,则返回成功
String result = "<xml>\n" +
" <return_code><![CDATA[SUCCESS]]></return_code>\n" +
" <return_msg><![CDATA[OK]]></return_msg>\n" +
"</xml>";
return ResponseEntity.ok(result);
}
}
pom:
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.9.6</version>
</dependency>
service:
校验的方法上一篇 payHelper有写,这里是直接调用。
/**
* 微信支付成功回调
*
* @param msg
*/
public void handleNotify(Map<String, String> msg) {
// 校验数据
payHelper.checkResultCode(msg);
// 校验签名
payHelper.checkSignature(msg);
// 校验金额
String totalFeeStr = msg.get("total_fee");
String outTradeNo = msg.get("out_trade_no"); //订单编号
if (StringUtils.isEmpty(totalFeeStr) || StringUtils.isEmpty(outTradeNo)) {
throw new LyException(ExceptionEnum.WX_PAY_NOTIFY_PARAM_ERROR);
}
// 获取结果中的金额
Long totalFee = Long.valueOf(totalFeeStr);
// 获取订单金额
Long orderId2 = Long.valueOf(outTradeNo);
Order order2 = orderMapper.selectByPrimaryKey(orderId2);
if (totalFee !=/*order2.getActualPay()*/ 1l) {
throw new LyException(ExceptionEnum.WX_PAY_NOTIFY_PARAM_ERROR);
}
// 修改订单状态
OrderStatus orderStatus = new OrderStatus();
orderStatus.setStatus(OrderStatusEnum.PAYED.value());
orderStatus.setOrderId(orderId2);
orderStatus.setCreateTime(new Date());
int count = statusMapper.updateByPrimaryKeySelective(orderStatus);
if (count != 1) {
throw new LyException(ExceptionEnum.UPDATE_ORDER_STATUS_FAILED);
}
log.info("[订单回调],订单支付成功,订单编号:{}", orderId2);
}
到这里也给微信发送了回复,我们前台要校验支付状态给用户反馈了。
查询支付状态(给用户的反馈)
前台要隔一段时间检查支付状态,给用户一个反馈,不能付完钱还是那个界面。
1 前端一直循环发送查询需求,然后这里接收订单编号进行查询
controller:
/**
* 查询订单支付状态
*
* @param orderId
* @return
*/
@GetMapping("state/{id}")
public ResponseEntity<Integer> queryOrderState(@PathVariable("id") Long orderId) {
return ResponseEntity.ok(orderService.queryOrderState(orderId).getValue());
}
2 业务层处理
service:
如果没查到,去微信里主动查一下(虽然它会主动发送状态,但万一没发自己查查也好)
public PayStateEnum queryOrderState(Long orderId) {
// 查询订单状态
OrderStatus orderStatus = statusMapper.selectByPrimaryKey(orderId);
Integer status = orderStatus.getStatus();
// 判断是否已支付
if (status != OrderStatusEnum.UNPAY.value()) {
return PayStateEnum.SUCCESS;
}
// 如果未支付,则去微信查询
return payHelper.queryPayState(orderId);
}
在PayHelper里面写逻辑:
上来就是一顿校验。
/**
* 主动查询订单状态
*
* @param orderId
*/
public PayStateEnum queryPayState(Long orderId) {
try {
// 组织请求参数
HashMap<String, String> data = new HashMap<>();
// 订单号
data.put("out_trade_no", orderId.toString());
// 查询状态
Map<String, String> result = wxPay.orderQuery(data);
/**
* 然后就是一顿校验
*/
checkResultCode(result);
checkSignature(result);
// 还有一大堆校验,将orderservice的校验金额和它的修改搬到这里来,通过
// result.get("trade_state");获取微信该订单的状态,然后修改数据库里的订单状态
// 一顿操作猛如虎,懒得弄
} catch (Exception e) {
return PayStateEnum.NOT_PAY;
}
return PayStateEnum.NOT_PAY;
}
到这里,查询状态也结束了,完整的流程也结束了。