正值青春的年纪,却过着每天学习的无聊生活。
一 支付的种类
1 微信支付
1.1 小程序支付
- 用户通过分享或扫描二维码进入商户小程序,用户选择购买,完成选购流程;
- 调起微信支付控件,用户开始输入支付密码;
密码验证通过,支付成功。商户后台得到支付成功的通知;
返回商户小程序,显示购买成功;
微信支付公众号下发支付凭证。
(1)生成支付订单
(2)用户付款
付款
@Login
@PostMapping("/microAppPayOrder")
@ApiOperation("小程序付款")
public R microAppPayOrder(@RequestBody PayOrderForm form, @RequestHeader HashMap header) {
ValidatorUtils.validateEntity(form);
String token = header.get("token").toString();
Long userId = Long.parseLong(jwtUtils.getClaimByToken(token).getSubject());
int orderId = form.getOrderId();
UserEntity user = new UserEntity();
user.setUserId(userId);
QueryWrapper wrapper = new QueryWrapper(user);
long count = userService.count(wrapper);
//判断用户是否存在
if (count == 0) {
return R.error("用户不存在");
}
//获取用户账号的openid字符串
String openId = userService.getOne(wrapper).getOpenId();
//判断用户是否拥有这个订单,并且订单是未支付状态
OrderEntity order = new OrderEntity();
order.setUserId(userId.intValue());
order.setId(orderId);
order.setStatus(1);
wrapper = new QueryWrapper(order);
count = orderService.count(wrapper);
if (count == 0) {
return R.error("不是有效的订单");
}
//验证购物券是否有效
//验证团购活动是否有效
//查询订单详情信息
order = new OrderEntity();
order.setId(orderId);
wrapper = new QueryWrapper(order);
order = orderService.getOne(wrapper); //订单对象
String amount = order.getAmount().multiply(new BigDecimal("100")).intValue() + ""; //订单金额
try {
//向微信平台发出请求,创建支付订单
WXPay wxPay = new WXPay(myWXPayConfig);
HashMap map = new HashMap();
map.put("nonce_str", WXPayUtil.generateNonceStr()); //随机字符串
map.put("body", "订单备注");
map.put("out_trade_no", order.getCode()); //商品订单流水号
map.put("total_fee", amount); //订单金额
map.put("spbill_create_ip", "127.0.0.1"); //客户端IP
map.put("notify_url", "http://66931e50.nat1.nsloop.com/renren-fast/app/wx/recieveMessage"); //通知回调地址
map.put("trade_type", "JSAPI"); //调用接口类型
map.put("openid", openId); //用户授权
Map<String, String> result = wxPay.unifiedOrder(map); //创建支付订单
String prepayId = result.get("prepay_id"); //获取支付订单ID
// System.out.println(prepayId);
if (prepayId != null) {
//更新本地商品订单信息,但是不更新商品订单状态
order.setPrepayId(prepayId); //保存支付订单ID
order.setPaymentType(1); //支付类型
UpdateWrapper updateWrapper = new UpdateWrapper();
updateWrapper.eq("id", order.getId());
orderService.update(order, updateWrapper);
//对返回给小程序的数据生成数字签名
map.clear();
map.put("appId", appId);
String timeStamp = new Date().getTime() + "";
map.put("timeStamp", timeStamp);
String nonceStr = WXPayUtil.generateNonceStr();
map.put("nonceStr", nonceStr);
map.put("package", "prepay_id=" + prepayId);
map.put("signType", "MD5");
String paySign = WXPayUtil.generateSignature(map, key); //生成数字签名
return R.ok().put("package", "prepay_id=" + prepayId)
.put("timeStamp", timeStamp)
.put("nonceStr", nonceStr)
.put("paySign", paySign);
} else {
return R.error("支付订单创建失败");
}
} catch (Exception e) {
e.printStackTrace();
return R.error("微信支付模块故障");
}
}
接收回调信息
@ApiOperation("接收消息通知")
@RequestMapping("/recieveMessage")
public void recieveMessage(HttpServletRequest request, HttpServletResponse response) throws Exception {
request.setCharacterEncoding("utf-8");
Reader reader = request.getReader();
BufferedReader buffer = new BufferedReader(reader);
String line = buffer.readLine();
StringBuffer temp = new StringBuffer();
while (line != null) {
temp.append(line);
line = buffer.readLine();
}
buffer.close();
reader.close();
String xml=temp.toString();
if(WXPayUtil.isSignatureValid(xml,key)){
Map<String, String> map = WXPayUtil.xmlToMap(temp.toString());
String resultCode = map.get("result_code");
String returnCode = map.get("return_code");
if ("SUCCESS".equals(resultCode) && "SUCCESS".equals(returnCode)) {
String outTradeNo = map.get("out_trade_no");
UpdateWrapper wrapper = new UpdateWrapper();
wrapper.eq("code", outTradeNo);
wrapper.set("status", 2);
// 更新订单支付状态
orderService.update(wrapper);
response.setCharacterEncoding("utf-8");
response.setContentType("application/xml");
Writer writer = response.getWriter();
BufferedWriter bufferedWriter = new BufferedWriter(writer);
bufferedWriter.write("<xml><return_code><![CDATA[SUCCESS]]></return_code> <return_msg><![CDATA[OK]]></return_msg></xml>");
bufferedWriter.close();
writer.close();
}
}
else{
response.sendError(500,"数字签名异常");
}
}
如果微信平台出现技术故障,支付模块正常,但是消息模块出了问题,那么就会出现支付成功,但是消息发布出去。商户系统自然也就收不到支付结果的通知了。
如果其中一端出现了网络故障,商户系统都不能收到消息通知。
商户系统接收不到支付结果的时候,需要主动去查询支付的结果,到底成功了还是失败了?商户系统什么时候应该发起主动查询呢?难道还要写个定时程序吗?
当然不需要。当小程序上面提示支付成功以后,那么小程序就像商户系统发出 Ajax 请求,告诉商户系统我已经收到付款成功的通知啦,你也去查询一下,这时候商户系统才需要发起主动查询。在小程序上面的SUCCESS回调函能执行,就意味着支付成功了。我们在SUCCESS回调函数里面向商户系统发起Ajax请求即可。商户平台接收到小程序的请求之后,根据支付订单 ID 去查询微信平台,如果已经付款成功,就修改本地商品订单为已支付状态。
主动查询支付结果
@Login
@PostMapping("/updateOrderStatus")
@ApiOperation("更新商品订单状态")
public R updateOrderStatus(@RequestBody UpdateOrderStatusForm form,
@RequestHeader HashMap header) {
ValidatorUtils.validateEntity(form);
String token = header.get("token").toString();
int userId = Integer.parseInt(jwtUtils.getClaimByToken(token).getSubject());
int orderId = form.getOrderId();
OrderEntity orderEntity = new OrderEntity();
orderEntity.setUserId(userId);
orderEntity.setId(orderId);
QueryWrapper wrapper = new QueryWrapper(orderEntity);
int count = orderService.count(wrapper);
if (count == 0) {
return R.error("用户与订单不匹配");
}
orderEntity = orderService.getOne(wrapper);
String code = orderEntity.getCode();
HashMap map = new HashMap();
map.put("appid", appId);
map.put("mch_id", mchId);
map.put("out_trade_no", code);
map.put("nonce_str", WXPayUtil.generateNonceStr());
try {
String sign = WXPayUtil.generateSignature(map, key);
map.put("sign", sign);
WXPay wxPay = new WXPay(myWXPayConfig);
Map<String, String> result = wxPay.orderQuery(map);
String returnCode = result.get("return_code");
String resultCode = result.get("result_code");
if ("SUCCESS".equals(returnCode) && "SUCCESS".equals(resultCode)) {
String tradeState = result.get("trade_state");
if ("SUCCESS".equals(tradeState)) {
UpdateWrapper updateWrapper = new UpdateWrapper();
updateWrapper.eq("code", code);
updateWrapper.set("status", 2);
updateWrapper.set("payment_type",1);
orderService.update(updateWrapper);
return R.ok("订单状态已修改");
} else {
return R.ok("订单状态未修改");
}
}
return R.ok("订单状态未修改");
} catch (Exception e) {
e.printStackTrace();
return R.error("查询支付订单失败");
}
}
1.2 微信 Native 支付
- 商户根据微信支付的规则,为不同商品生成不同的二维码,展示在各种场景,用于用户扫描购买;
- 用户使用微信“扫一扫”扫描二维码后,获取商品支付信息;
- 用户确认支付,输入支付密码;
- 支付完成后会提示用户支付成功,商户后台得到支付成功的通知。
(1)生成支付订单
生成支付订单
@Login
@PostMapping("/nativePayOrder")
@ApiOperation("native付款")
public R nativePayOrder(@RequestBody PayOrderForm form, @RequestHeader HashMap header) {
ValidatorUtils.validateEntity(form);
String token = header.get("token").toString();
Long userId = Long.parseLong(jwtUtils.getClaimByToken(token).getSubject());
int orderId = form.getOrderId();
UserEntity user = new UserEntity();
user.setUserId(userId);
QueryWrapper wrapper = new QueryWrapper(user);
long count = userService.count(wrapper);
if (count == 0) {
return R.error("用户不存在");
}
OrderEntity order = new OrderEntity();
order.setUserId(userId.intValue());
order.setId(orderId);
order.setStatus(1);
wrapper = new QueryWrapper(order);
count = orderService.count(wrapper);
if (count == 0) {
return R.error("不是有效的订单");
}
//验证购物券是否有效
//验证团购活动是否有效
order = new OrderEntity();
order.setId(orderId);
wrapper = new QueryWrapper(order);
order = orderService.getOne(wrapper);
//向微信平台发出请求,创建支付订单
String amount = order.getAmount().multiply(new BigDecimal("100")).intValue() + "";
try {
WXPay wxPay = new WXPay(myWXPayConfig);
HashMap map = new HashMap();
map.put("nonce_str", WXPayUtil.generateNonceStr()); //随机字符串
map.put("body", "订单备注");
map.put("out_trade_no", order.getCode());
map.put("total_fee", amount);
map.put("spbill_create_ip", "127.0.0.1");
map.put("notify_url", "https://127.0.0.1/test");
map.put("trade_type", "NATIVE");
String sign=WXPayUtil.generateSignature(map,key);
map.put("sign",sign);
Map<String, String> result = wxPay.unifiedOrder(map);
String prepayId = result.get("prepay_id");
// System.out.println(prepayId);
String codeUrl=result.get("code_url");
if (prepayId != null) {
order.setPrepayId(prepayId);
UpdateWrapper updateWrapper = new UpdateWrapper();
updateWrapper.eq("id", order.getId());
orderService.update(order, updateWrapper);
return R.ok().put("codeUrl", codeUrl);
} else {
return R.error("支付订单创建失败");
}
} catch (Exception e) {
e.printStackTrace();
return R.error("微信支付模块故障");
}
}
生成二维码
@GetMapping("/qrcode")
public void qrcode(HttpServletRequest request,HttpServletResponse response) throws Exception{
String codeUrl=request.getParameter("codeUrl");
if(codeUrl!=null&&codeUrl.length()>0){
QrConfig qrConfig=new QrConfig();
qrConfig.setWidth(250);
qrConfig.setHeight(250);
qrConfig.setMargin(2);
OutputStream out=response.getOutputStream();
QrCodeUtil.generate(codeUrl,qrConfig,"jpg",out);
out.close();
}
}
(2)用户付款
网站或售货机与微信是两个平台,无法接收微信平台的支付结果的通知,需要前端定时主动查询订单支付结果
@Login
@PostMapping("/searchOrderStatus")
@ApiOperation("查询支付订单状态")
public R searchOrderStatus(@RequestBody SearchOrderStatusForm form,
@RequestHeader HashMap header){
ValidatorUtils.validateEntity(form);
String token = header.get("token").toString();
int userId = Integer.parseInt(jwtUtils.getClaimByToken(token).getSubject());
int orderId = form.getOrderId();
OrderEntity orderEntity = new OrderEntity();
orderEntity.setUserId(userId);
orderEntity.setId(orderId);
QueryWrapper wrapper = new QueryWrapper(orderEntity);
int count = orderService.count(wrapper);
if (count == 0) {
return R.error("用户与订单不匹配");
}
orderEntity = orderService.getOne(wrapper);
String code = orderEntity.getCode();
HashMap map = new HashMap();
map.put("appid", appId);
map.put("mch_id", mchId);
map.put("out_trade_no", code);
map.put("nonce_str", WXPayUtil.generateNonceStr());
try {
String sign = WXPayUtil.generateSignature(map, key);
map.put("sign", sign);
WXPay wxPay = new WXPay(myWXPayConfig);
Map<String, String> result = wxPay.orderQuery(map);
String returnCode = result.get("return_code");
String resultCode = result.get("result_code");
if ("SUCCESS".equals(returnCode) && "SUCCESS".equals(resultCode)) {
String tradeState = result.get("trade_state");
if ("SUCCESS".equals(tradeState)) {
UpdateWrapper updateWrapper = new UpdateWrapper();
updateWrapper.eq("code", code);
updateWrapper.set("status", 2);
updateWrapper.set("payment_type",1);
orderService.update(updateWrapper);
return R.ok("订单状态已修改");
} else {
return R.ok("订单状态未修改");
}
}
return R.ok("订单状态未修改");
} catch (Exception e) {
e.printStackTrace();
return R.error("查询支付订单失败");
}
}
1.3 微信扫码支付
步骤1:用户选择付款码支付,付款码打开路径:微信->“我”->“支付”->“收付款”;
步骤2:收银员在商户系统操作生成支付订单,用户确认支付金额;
步骤3:商户收银员用扫码设备扫描用户的条码/二维码,商户收银系统提交支付;
步骤4:微信支付后台系统收到支付请求,根据验证密码规则判断是否验证用户的支付密码,不需要验证密码的交易直接发起扣款,需要验证密码的交易会弹出密码输入框。支付成功后微信端会弹出成功页面,支付失败会弹出错误提示。
@Login
@PostMapping("/scanCodePayOrder")
@ApiOperation("付款码收款")
public R scanCodePayOrder(@RequestBody ScanCodePayOrderForm form, @RequestHeader HashMap header) {
ValidatorUtils.validateEntity(form);
String token = header.get("token").toString();
Long userId = Long.parseLong(jwtUtils.getClaimByToken(token).getSubject());
int orderId = form.getOrderId();
UserEntity user = new UserEntity();
user.setUserId(userId);
QueryWrapper wrapper = new QueryWrapper(user);
long count = userService.count(wrapper);
if (count == 0) {
return R.error("用户不存在");
}
OrderEntity order = new OrderEntity();
order.setUserId(userId.intValue());
order.setId(orderId);
order.setStatus(1);
wrapper = new QueryWrapper(order);
count = orderService.count(wrapper);
if (count == 0) {
return R.error("不是有效的订单");
}
//验证购物券是否有效
//验证团购活动是否有效
order = new OrderEntity();
order.setId(orderId);
wrapper = new QueryWrapper(order);
order = orderService.getOne(wrapper);
//向微信平台发出请求,创建支付订单
String amount = order.getAmount().multiply(new BigDecimal("100")).intValue() + "";
try {
WXPay wxPay = new WXPay(myWXPayConfig);
HashMap map = new HashMap();
map.put("appid",appId);
map.put("mch_id",mchId);
map.put("nonce_str", WXPayUtil.generateNonceStr()); //随机字符串
map.put("body", "订单备注");
map.put("out_trade_no", order.getCode());
map.put("total_fee", amount);
map.put("spbill_create_ip", "127.0.0.1");
map.put("auth_code",form.getAuthCode());
String sign=WXPayUtil.generateSignature(map,key);
map.put("sign",sign);
Map<String, String> result = wxPay.microPay(map);
String returnCode = result.get("return_code");
String resultCode = result.get("result_code");
if ("SUCCESS".equals(returnCode) && "SUCCESS".equals(resultCode)) {
String prepayId = result.get("transaction_id");
order.setPrepayId(prepayId);
order.setStatus(2);
order.setPaymentType(1);
UpdateWrapper updateWrapper = new UpdateWrapper();
updateWrapper.eq("id", order.getId());
orderService.update(order, updateWrapper);
return R.ok("付款成功");
}
else {
return R.error("付款失败");
}
} catch (Exception e) {
e.printStackTrace();
return R.error("微信支付模块故障");
}
}
二 开发中遇到的支付问题
问题1 用户已支付,然后取消订单,没有退款
1.1 问题梳理分析
订单号:SA012104120809590003430563,订单创建时间:2021-04-12 08:09:59,订单支付时间:2021-04-12 08:14:00,订单取消时间:2021-04-12 08:25:12。由于支付过程为异步且和第三方存在交互,存在订单状态更新延时的情况,而在用户取消的一些特殊场景(通过小程序日志分析,该用户在支付完成后跳转到订单页,订单仍然是未支付状态,而此时用户可以取消订单;一般场景应该是支付完成跳转到订单页,订单为已支付,而此时用户是不可以取消订单的),未对取消操作做完整的校验和拦截,导致用户在支付的情况下取消订单,但确未返回现金的情况。
1.2 同样问题订单查询
通过系统查询发现同样的类型单有16单,涉及总金额 1383 元;
1.3 问题修复方案
(1)经过运营,研发等必要核查,将金额退回给用户;
(2)系统对于取消订单操作做必要的前置校验,加入判断订单是否已经支付,已经支付则前置拦截并且让客户感知。(备注:支付完成的订单走的售后流程)