700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > 微信V3版本支付下单 查询支付订单状态 订单退款接入正式项目中并引入策略模式实操

微信V3版本支付下单 查询支付订单状态 订单退款接入正式项目中并引入策略模式实操

时间:2021-03-25 06:19:54

相关推荐

微信V3版本支付下单 查询支付订单状态 订单退款接入正式项目中并引入策略模式实操

开篇介绍:

一、策略模式(Strategy Pattern)的概念

二、应用场景举例

三、角色

​四、服务端OR客户端生成支付等二维码优缺点分析

五、 多渠道支付对接-策略模式+简单工厂模式编码实操

六、查询支付订单状态

七、再看下订单退款实操

开篇介绍:

本文介绍微信支付中的Native支付方式,版本是APIv3,其中Native和JSAPI的区别如下

Native支付:商家在系统中按微信支付协议生成支付二维码,用户扫码拉起微信收银台,确认并完成付款

JSAPI支付:商家张贴收款码物料,用户打开扫一扫,扫码后输入金额,完成付款

以下内容增加了支付接口调和策略模式的使用,支付的VO类,大家可以参照自己公司的VO字段,需要改动支付的部分业务参数即可。

注意:微信支付个人无法对接操作,需要有公司商户账号,一般开发过程中是产品经理或者相关负责人提供。

我们来看下支付成功和退款成功的截图,看完本博文后,你将学会在实际项目中完成微信支付。

首先我们将先复习下策略模式,前面4步骤都是策略模式,后面几步骤是代码实操,不要急,一步步来。

一、策略模式(Strategy Pattern)的概念

定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换

比如说淘宝天猫双十一,正在搞活动有打折的、有满减的、有返利的等等,这些算法只是一种策略,并且是随时都可能互相替换的, 我们就可以定义一组算法,将每个算法都封装起来,并且使它们之间可以互换

二、应用场景举例

。老王计划外出旅游,选择骑自行车、坐汽车、飞机等,每一种旅行方式都是一个策略

。如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么可以使用策略模式

。 不希望暴露复杂的、与算法有关的数据结构,那么可以使用策略模式来封装算法

。对接第三方支付里面,微信支付、支付宝支付等都可以是一种策略

三、角色

Context上下文:屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化(相当于中介)

Strategy策略角色:抽象策略角色,是对策略、算法家族的抽象,定义每个策略或算法必须具有的方法和属性

ConcreteStrategy具体策略角色:用于实现抽象策略中的操作,即实现具体的算法

四、服务端OR客户端生成支付等二维码优缺点分析

服务端生成二维码

优点:

引入二维码jar包统一生成

图片生成兼容性好,传输相对安全不易被篡改

缺点:

占据服务端CPU内存/资源、消耗传输带宽,性能较差

没法一并返回其他参数,定制化相对弱

客户端生成二维码(建议使用)

优点

在客户端生成二维码会降低服务器的运算与存储压力

选择框架要考虑兼容性,客户端根据js框架生成二维码

后端返回 二维码文本值,还可以携带其他参数(比如订单号,可以轮训支付状态)

缺点

客户端需要引入二维码生成js插件

需要考虑浏览器兼容性

五、 多渠道支付对接-策略模式+简单工厂模式编码实操

策略模式代码骨架分为以下几种:

策略接口开发 PayStrategy

策略上下文 PayStrategyContext开发

具体支付策略开发 AlipayStrategy、WechatPayStrategy

简单工厂类开发;

首先第一步我们从impl调用层开始倒推:

//这个是在实际service实现类中的调用,下面这段代码被controller层调用,//然后返回给前端,前端根据返回的code_url展示二维码信息@Autowiredprivate PayFactory payFactory;//调用支付信息String codeUrl = payFactory.pay(payInfoVO);if(StringUtils.isNotBlank(codeUrl)){Map<String,String> resultMap = new HashMap<>(2);resultMap.put("code_url",codeUrl);resultMap.put("out_trade_no",payInfoVO.getOutTradeNo());return JsonData.buildSuccess(resultMap);}

第二步再来看下PayFactory支付工厂类,里面的支付 做了什么:

package ponent;import lombok.extern.slf4j.Slf4j;import net.wnn.enums.ProductOrderPayTypeEnum;import net.wnn.vo.PayInfoVO;import org.springframework.beans.factory.annotation.Autowired;import org.ponent;@Component@Slf4jpublic class PayFactory {@Autowiredprivate AliPayStrategy aliPayStrategy;@Autowiredprivate WechatPayStrategy wechatPayStrategy;/*** 创建支付,简单工厂模式* @param payInfoVO* @return*/public String pay(PayInfoVO payInfoVO){String payType = payInfoVO.getPayType();if (ProductOrderPayTypeEnum.ALI_PAY.name().equals(payType)) {//支付宝支付PayStrategyContext payStrategyContext = new PayStrategyContext(aliPayStrategy);return payStrategyContext.executeUnifiedOrder(payInfoVO);} else if(ProductOrderPayTypeEnum.WECHAT_PAY.name().equals(payType)){//微信支付PayStrategyContext payStrategyContext = new PayStrategyContext(wechatPayStrategy);return payStrategyContext.executeUnifiedOrder(payInfoVO);}return "";}/*** 关闭订单* @param payInfoVO* @return*/public String closeOrder(PayInfoVO payInfoVO){String payType = payInfoVO.getPayType();if (ProductOrderPayTypeEnum.ALI_PAY.name().equals(payType)) {//支付宝支付PayStrategyContext payStrategyContext = new PayStrategyContext(aliPayStrategy);return payStrategyContext.executeCloseOrder(payInfoVO);} else if(ProductOrderPayTypeEnum.WECHAT_PAY.name().equals(payType)){//微信支付PayStrategyContext payStrategyContext = new PayStrategyContext(wechatPayStrategy);return payStrategyContext.executeCloseOrder(payInfoVO);}return "";}/*** 查询支付状态* @param payInfoVO* @return*/public String queryPayStatus(PayInfoVO payInfoVO){String payType = payInfoVO.getPayType();if (ProductOrderPayTypeEnum.ALI_PAY.name().equals(payType)) {//支付宝支付PayStrategyContext payStrategyContext = new PayStrategyContext(aliPayStrategy);return payStrategyContext.executeQueryPayStatus(payInfoVO);} else if(ProductOrderPayTypeEnum.WECHAT_PAY.name().equals(payType)){//微信支付PayStrategyContext payStrategyContext = new PayStrategyContext(wechatPayStrategy);return payStrategyContext.executeQueryPayStatus(payInfoVO);}return "";}/*** 退款接口* @param payInfoVO* @return*/public String refund(PayInfoVO payInfoVO){String payType = payInfoVO.getPayType();if (ProductOrderPayTypeEnum.ALI_PAY.name().equals(payType)) {//支付宝支付PayStrategyContext payStrategyContext = new PayStrategyContext(aliPayStrategy);return payStrategyContext.executeRefund(payInfoVO);} else if(ProductOrderPayTypeEnum.WECHAT_PAY.name().equals(payType)){//微信支付PayStrategyContext payStrategyContext = new PayStrategyContext(wechatPayStrategy);return payStrategyContext.executeRefund(payInfoVO);}return "";}}

由上面代码可见,支付的工厂是用来根据支付方式调用具体的实现方法 。

第三步,这一步我们来看一下PayStrategyContext支付上下文是什么东西:

package ponent;import net.wnn.vo.PayInfoVO;public class PayStrategyContext {private PayStrategy payStrategy;public PayStrategyContext(PayStrategy payStrategy){this.payStrategy = payStrategy;}/*** 根据策略对象,执行不同的下单接口* @return*/public String executeUnifiedOrder(PayInfoVO payInfoVO){return payStrategy.unifiedOrder(payInfoVO);}/*** 根据策略对象,执行不同的退款接口* @return*/public String executeRefund(PayInfoVO payInfoVO){return payStrategy.refund(payInfoVO);}/*** 根据策略对象,执行不同的关闭接口* @return*/public String executeCloseOrder(PayInfoVO payInfoVO){return payStrategy.closeOrder(payInfoVO);}/*** 根据策略对象,执行不同的查询订单状态接口* @return*/public String executeQueryPayStatus(PayInfoVO payInfoVO){return payStrategy.queryPayStatus(payInfoVO);}}

标准对Context上下文的解释就是:屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化。通俗点说这就是个中介,根据传入的参数去调相应的实现接口,自己就干个调用

第四步、再看看PayStrategy

package ponent;import net.wnn.vo.PayInfoVO;public interface PayStrategy {/*** 统一下单接口* @param payInfoVO* @return*/String unifiedOrder(PayInfoVO payInfoVO);/*** 退款接口* @param payInfoVO* @return*/default String refund(PayInfoVO payInfoVO){ return ""; }/*** 查询支付状态* @param payInfoVO* @return*/default String queryPayStatus(PayInfoVO payInfoVO){ return ""; }/*** 关闭订单* @param payInfoVO* @return*/default String closeOrder(PayInfoVO payInfoVO){ return ""; }}

Strategy策略角色:抽象策略角色,是对策略、算法家族的抽象,定义每个策略或算法必须具有的方法和属性

第五步、再看看AliPayStrategy,这只是代码骨架,表明策略模式的实现方式,本文重点说的是微信支付,在第六步有

package ponent;import lombok.extern.slf4j.Slf4j;import net.wnn.vo.PayInfoVO;import org.springframework.stereotype.Service;@Service@Slf4jpublic class AliPayStrategy implements PayStrategy{@Overridepublic String unifiedOrder(PayInfoVO payInfoVO) {return null;}@Overridepublic String refund(PayInfoVO payInfoVO) {return null;}@Overridepublic String queryPayStatus(PayInfoVO payInfoVO) {return null;}@Overridepublic String closeOrder(PayInfoVO payInfoVO) {return null;}}

ConcreteStrategy就是策略模式中的具体策略角色:用于实现抽象策略中的操作,即实现具体的算法

第六步、本文用的是微信支付,那就再看下WechatPayStrategy 下单的代码逻辑

package ponent;import com.alibaba.fastjson.JSONObject;import lombok.extern.slf4j.Slf4j;import net.wnn.config.WechatPayApi;import net.wnn.config.WechatPayConfig;import net.wnn.vo.PayInfoVO;import org.apache.http.client.methods.CloseableHttpResponse;import org.apache.http.client.methods.HttpGet;import org.apache.http.client.methods.HttpPost;import org.apache.http.entity.StringEntity;import org.apache.http.impl.client.CloseableHttpClient;import org.apache.http.util.EntityUtils;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.http.HttpStatus;import org.springframework.stereotype.Service;import java.math.BigDecimal;import java.text.SimpleDateFormat;import java.util.Date;@Service@Slf4jpublic class WechatPayStrategy implements PayStrategy{@Autowiredprivate WechatPayConfig payConfig;@Autowiredprivate CloseableHttpClient wechatPayClient;/*** 统一下单接口* @param payInfoVO* @return*/@Overridepublic String unifiedOrder(PayInfoVO payInfoVO) {//过期时间 RFC 3339格式SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");//支付订单过期时间String timeExpire = sdf.format(new Date(System.currentTimeMillis() + payInfoVO.getOrderPayTimeoutMills()));JSONObject amountObj = new JSONObject();//数据库存储是double比如,100.99元,微信支付需要以分为单位int amount = payInfoVO.getPayFee().multiply(BigDecimal.valueOf(60)).intValue();amountObj.put("total", amount);amountObj.put("currency", "CNY");JSONObject payObj = new JSONObject();payObj.put("mchid", payConfig.getMchId());payObj.put("out_trade_no", payInfoVO.getOutTradeNo());payObj.put("appid", payConfig.getWxPayAppid());payObj.put("description", "王姑娘之微信支付增加策略模式后的下单验证");payObj.put("notify_url", payConfig.getCallbackUrl());payObj.put("time_expire", timeExpire);payObj.put("amount", amountObj);//回调携带payObj.put("attach", "{\"accountNo\":" + payInfoVO.getAccountNo() + "}");// 处理请求body参数String body = payObj.toJSONString();log.debug("请求参数:{}",body);StringEntity entity = new StringEntity(body,"utf-8");entity.setContentType("application/json");HttpPost httpPost = new HttpPost(WechatPayApi.NATIVE_ORDER);httpPost.setHeader("Accept","application/json");httpPost.setEntity(entity);String result = "";try(CloseableHttpResponse response = wechatPayClient.execute(httpPost)){//响应码int statusCode = response.getStatusLine().getStatusCode();//响应体String responseStr = EntityUtils.toString(response.getEntity());log.debug("下单响应码:{},响应体:{}",statusCode,responseStr);if(statusCode == HttpStatus.OK.value()){JSONObject jsonObject = JSONObject.parseObject(responseStr);if(jsonObject.containsKey("code_url")){result = jsonObject.getString("code_url");}}else {log.error("下单响应失败:{},响应体:{}",statusCode,responseStr);}}catch (Exception e){log.error("微信支付响应异常:{}",e);}return result;}}

第七步、下单验证截图

postman请求:

根据code_url生成的二维码,用微信客户端进行扫码支付:

下单支付接口调用的一些参数:

请求参数:{"time_expire":"-03-02T21:29:14+08:00","amount":{"total":60,"currency":"CNY"},"mchid":"1601xxxx","out_trade_no":"yb6gMTsnzyxxxxl4","appid":"wx5beac1xxxxc","description":"王姑娘之微信支付增加策略模式后的下单验证","attach":"{\"accountNo\":693232xxxx}","notify_url":"http://api.xxxx/shop-server/api/callback/order/v1/wechat"}

下单响应码:200,响应体:{"code_url":"weixin://wxpay/bizpayurl?pr=8L2yqCSzz"}

支付截图:

支付成功截图:

看到这你肯定有疑惑了,支付的那些参数等都是从哪来,怎么加载的?由于文章篇幅有限,基础的微信支付信息,写在了下面这篇博客中,可以直接点击查看,里面详细的介绍了微信支付的准备参数:

一文带你学会微信V3版本下单支付、退款、关单流程代码实操_8年开发工作经验的老王,积极分享工作中遇到的问题~-CSDN博客_wechatpay-apache-httpclient

六、查询支付订单状态

第一步实现类的调用:

@Overridepublic String queryProductOrderState(String outTradeNo) {PayInfoVO payInfoVO = PayInfoVO.builder().outTradeNo(outTradeNo).payType("WECHAT_PAY").build();String result = payFactory.queryPayStatus(payInfoVO);return result;}

第二步:

PayFactory的queryPayStatus方法,在上面下单的工厂类都有,这里不再赘述PayStrategyContext的executeQueryPayStatus方法,上面也有,这里不再赘述

第三步:支付订单查询的具体操作代码

package ponent;import com.alibaba.fastjson.JSONObject;import lombok.extern.slf4j.Slf4j;import net.wnn.config.WechatPayApi;import net.wnn.config.WechatPayConfig;import net.wnn.vo.PayInfoVO;import org.apache.http.client.methods.CloseableHttpResponse;import org.apache.http.client.methods.HttpGet;import org.apache.http.impl.client.CloseableHttpClient;import org.apache.http.util.EntityUtils;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.http.HttpStatus;import org.springframework.stereotype.Service;@Service@Slf4jpublic class WechatPayStrategy implements PayStrategy{@Autowiredprivate WechatPayConfig payConfig;@Autowiredprivate CloseableHttpClient wechatPayClient;/*** 微信支付查询订单状态* @param payInfoVO* @return*/@Overridepublic String queryPayStatus(PayInfoVO payInfoVO) {String outTradeNo = payInfoVO.getOutTradeNo();String url = String.format(WechatPayApi.NATIVE_QUERY,outTradeNo,payConfig.getMchId());HttpGet httpGet = new HttpGet(url);httpGet.setHeader("Accept","application/json");String result = "";try(CloseableHttpResponse response = wechatPayClient.execute(httpGet)){//响应码int statusCode = response.getStatusLine().getStatusCode();//响应体String responseStr = EntityUtils.toString(response.getEntity());log.info("查询响应码:{},响应体:{}",statusCode,responseStr);if(statusCode == HttpStatus.OK.value()){JSONObject jsonObject = JSONObject.parseObject(responseStr);if(jsonObject.containsKey("trade_state")){result = jsonObject.getString("trade_state");}}else {log.error("查询支付状态响应失败:{},响应体:{}",statusCode,responseStr);}}catch (Exception e){log.error("微信支付响应异常:{}",e);}return result;}}

第四步 postman请求查询接口接口 传参 支付订单ID

查询返回结果: 订单支付成功

查询响应码:200,响应体:{"amount":{"currency":"CNY","payer_currency":"CNY","payer_total":60,"total":60},"appid":"wx5xxxc","attach":"{\"accountNo\":693232391484866560}","bank_type":"OTHERS","mchid":"16016xxxx",

"out_trade_no":"yb6gMTsnzyNrjb5iDa4Oxxx","payer":{"openid":"oiNKG04xxx"},"promotion_detail":[],"success_time":"-03-02T20:59:36+08:00","trade_state":"SUCCESS","trade_state_desc":"支付成功","trade_type":"NATIVE","transaction_id":"420000136303029043441628"}

七、再看下订单退款实操

第一步实现类的调用:

@Overridepublic String executeRefund(String outTradeNo) {PayInfoVO payInfoVO = PayInfoVO.builder().outTradeNo(outTradeNo).payType("WECHAT_PAY").build();String result = payFactory.refund(payInfoVO);return result;}

第二步:

PayFactory的refund方法,在上面下单的工厂类都有,这里不再赘述

PayStrategyContext的executeRefund方法,上面也有,这里不再赘述

第三步:支付订单退款的具体操作代码

package ponent;import com.alibaba.fastjson.JSONObject;import lombok.extern.slf4j.Slf4j;import net.wnn.config.WechatPayApi;import net.wnn.config.WechatPayConfig;import net.monUtil;import net.wnn.vo.PayInfoVO;import org.apache.http.client.methods.CloseableHttpResponse;import org.apache.http.client.methods.HttpGet;import org.apache.http.client.methods.HttpPost;import org.apache.http.entity.StringEntity;import org.apache.http.impl.client.CloseableHttpClient;import org.apache.http.util.EntityUtils;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.http.HttpStatus;import org.springframework.stereotype.Service;import java.math.BigDecimal;import java.text.SimpleDateFormat;import java.util.Date;@Service@Slf4jpublic class WechatPayStrategy implements PayStrategy{@Autowiredprivate WechatPayConfig payConfig;@Autowiredprivate CloseableHttpClient wechatPayClient;@Overridepublic String refund(PayInfoVO payInfoVO) {String outTradeNo = "yb6gMTsnzyNrjb5iDa4OYFwiRc5nh7l4";String refundNo = CommonUtil.getStringNumRandom(32);// 请求body参数JSONObject refundObj = new JSONObject();//订单号refundObj.put("out_trade_no", outTradeNo);//退款单编号,商户系统内部的退款单号,商户系统内部唯一,// 只能是数字、大小写字母_-|*@ ,同一退款单号多次请求只退一笔refundObj.put("out_refund_no", refundNo);refundObj.put("reason","商品已售完需要退款");refundObj.put("notify_url", payConfig.getCallbackUrl());JSONObject amountObj = new JSONObject();//退款金额amountObj.put("refund", 60);//实际支付的总金额amountObj.put("total", 60);amountObj.put("currency", "CNY");refundObj.put("amount", amountObj);String body = refundObj.toJSONString();log.info("请求参数:{}",body);StringEntity entity = new StringEntity(body,"utf-8");entity.setContentType("application/json");HttpPost httpPost = new HttpPost(WechatPayApi.NATIVE_REFUND_ORDER);httpPost.setHeader("Accept","application/json");httpPost.setEntity(entity);try(CloseableHttpResponse response = wechatPayClient.execute(httpPost)){//响应码int statusCode = response.getStatusLine().getStatusCode();//响应体String responseStr = EntityUtils.toString(response.getEntity());log.info("申请订单退款响应码:{},响应体:{}",statusCode,responseStr);}catch (Exception e){e.printStackTrace();}return "退款成功";}}

退款成功图片:

退款日志消息:

请求参数:{"reason":"商品已售完需要退款","amount":{"total":60,"currency":"CNY","refund":60},"out_trade_no":"yb6gMTsnzyNrjb5iDa4xxx","out_refund_no":"RbpQWZntjoxxxx","notify_url":"http://api.oxxxx/shop-server//wechat"}

申请订单退款响应码:200,响应体:{"amount":{"currency":"CNY","discount_refund":0,"from":[],"payer_refund":60,"payer_total":60,"refund":60,"settlement_refund":60,"settlement_total":60,"total":60},"channel":"ORIGINAL","create_time":"-03-02T21:25:00+08:00","funds_account":"AVAILABLE","out_refund_no":"RbpQWZntjoxxxxq","out_trade_no":"yb6gMTsnzyNrjb5iDa4xxxx","promotion_detail":[],"refund_id":"5030040098xxxx","status":"PROCESSING","transaction_id":"420000136320xxx","user_received_account":"支付用户零钱"}

到这微信Native支付方式的下单、查询、退款操作就整合进实际开发项目中啦,大家引用到自己项里面的时候,只需要改动支付的部分业务参数即可。

基础参数不知道从哪来,证书不知道怎么加载的,看下下面这篇博客,跟本博客代码内容是上下两集:

一文带你学会微信V3版本下单支付、退款、关单流程代码实操_8年开发工作经验的老王,积极分享工作中遇到的问题~-CSDN博客_wechatpay-apache-httpclient本文介绍微信支付中的Native支付方式,版本是APIv3,其中Native和JSAPI的区别如下Native支付:商家在系统中按微信支付协议生成支付二维码,用户扫码拉起微信收银台,确认并完成付款JSAPI支付:商家张贴收款码物料,用户打开扫一扫,扫码后输入金额,完成付款以下博文没有掺杂过多业务逻辑,对象数据都是固定写的,方便将注意力集中在微信支付的接口使用上。正式对接到项目里面,只需要改动支付的部分业务参数即可。微信支付的文档还是很详细的,建议多看几遍,以下博文也都是根据官方文档的描述来操/wnn654321/article/details/122933324

微信支付回调验签流程

微信支付V3版本回调+验签流程_8年开发工作经验的老王,积极分享工作中遇到的问题~-CSDN博客回调验签流程介绍 官方文档 https://pay./wiki/doc/apiv3/apis/chapter3_4_5.shtml https://pay./wiki/doc/apiv3/wechatpay/wechatpay4_1.shtml注意: 同样的通知可能会多次发送给商户系统,商户系统必须能够正确处理重复的通知 确保回调URL是外部可正常访问的,且不能携带后缀参数/wnn654321/article/details/123298162

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。