微信JSAPI支付对接

简介

JSAPI支付是指商户通过调用微信支付提供的JSAPI接口,在支付场景中调起微信支付模块完成收款。

应用场景

JSAPI支付适用于线下场所、公众号场景和PC网站场景。

商户已有H5商城网站,用户通过消息或扫描二维码在微信内打开网页时,可以调用微信支付完成下单购买的流程。具体操作流程如下:

1.商户下发图文消息或者通过自定义菜单吸引用户点击进入商户网页
2.进入商户网页,用户选择购买,完成选购流程。
3.调起微信支付控件,用户开始输入支付密码
4.密码验证通过,支付成功。商户后台得到支付成功的通知
5.返回商户页面,显示购买成功。该页面由商户自定义
6.微信支付公众号下发支付凭证

接入前准备

直接跳转微信支付商户平台 https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_1.shtml

生成密钥文件:
在这里插入图片描述

配置文件:

wx:appId: appIdkeyPath: apiclient_key.pemcertPath: apiclient_cert.pemcertP12Path: 暂不用platformCertPath: platform_cert.pemmchId: mchIdapiKey3: 暂不用apiKey: apiKeydomain: https://hous.exchang.cnserialNo: 序列号

pom文件:

<dependency><groupId>com.github.wechatpay-apiv3</groupId><artifactId>wechatpay-java</artifactId><version>0.2.2</version></dependency>

代码:

配置类:

//获取yml中微信配置
@Data
@ConfigurationProperties(prefix = "wx")
public class WechatProperties {private String appId;private String keyPath;private String certPath;private String platformCertPath;private String mchId;private String apiKey;private String domain;private String serialNo;
}
//配置类
@Configuration
@EnableConfigurationProperties(WechatProperties.class)
public class WechatJsapiConfig {private final WechatProperties wechatProperties;public WechatJsapiConfig(WechatProperties wechatProperties) {this.wechatProperties = wechatProperties;}@Beanpublic RSAConfig rsaConfig() throws IOException {ClassPathResource keyResource = new ClassPathResource(wechatProperties.getKeyPath());String apiClientKey = keyResource.getFile().getPath();ClassPathResource certResource = new ClassPathResource(wechatProperties.getPlatformCertPath());String platformCertResourceKey = certResource.getFile().getPath();/*String apiClientKey = wechatProperties.getKeyPath();
//        ClassPathResource certResource = new ClassPathResource(wechatProperties.getPlatformCertPath());String platformCertResourceKey = wechatProperties.getPlatformCertPath();*/return new RSAConfig.Builder().merchantId(wechatProperties.getMchId()).privateKeyFromPath(apiClientKey).merchantSerialNumber(wechatProperties.getSerialNo())//平台证书.wechatPayCertificatesFromPath(platformCertResourceKey).build();}@Beanpublic JsapiServiceExtension jsapiService() throws IOException {return new JsapiServiceExtension.Builder().config(this.rsaConfig()).build();}@Beanpublic NotificationParser notificationParser() throws IOException{ClassPathResource certResource = new ClassPathResource(wechatProperties.getPlatformCertPath());String platformCertResourceKey = certResource.getFile().getPath();//String platformCertResourceKey = wechatProperties.getPlatformCertPath();NotificationConfig config = new RSANotificationConfig.Builder().apiV3Key(wechatProperties.getApiKey()).certificatesFromPath(platformCertResourceKey).build();// 初始化 NotificationParserreturn new NotificationParser(config);}@Bean(name = "payRefundService")public RefundService refundService() throws IOException {return new RefundService.Builder().config(this.rsaConfig()).build();}}

创建支付单:

@Slf4j
@Service
@EnableConfigurationProperties(WechatProperties.class)
public class WechatJsapiPayServiceImpl implements BasePayService {@Resourceprivate WechatProperties wechatProperties;@Resourceprivate JsapiServiceExtension jsapiService;@Value("${spring.application.name}")private String serviceName;@Resourceprivate MemberDao memberDao;@Resourceprivate RedisCache redisCache;@Resourceprivate ShopDao shopDao;@Overridepublic R<?> paymentOrder(List<Order> orderList) {BigDecimal total = orderList.stream().map(Order::getPayAmount).reduce(BigDecimal.ZERO, BigDecimal::add);Order order = orderList.get(0);Long mId = order.getMId();Member member = memberDao.queryById(mId);Integer payTotal = total.multiply(BigDecimal.valueOf(100)).intValue();PrepayRequest prepayRequest = new PrepayRequest();prepayRequest.setAppid(wechatProperties.getAppId());prepayRequest.setMchid(wechatProperties.getMchId());prepayRequest.setDescription("供应链下单");prepayRequest.setOutTradeNo(order.getBatchSn());prepayRequest.setNotifyUrl(wechatProperties.getDomain() + "/callback/wechat/pay");prepayRequest.setAttach("1");prepayRequest.setTimeExpire(this.getTimeExpire());Amount amount = new Amount();amount.setTotal(payTotal);Payer payer = new Payer();payer.setOpenid(member.getOpenId());prepayRequest.setAmount(amount);prepayRequest.setPayer(payer);log.info("创建预支付单入参:【{}】", JSONObject.toJSONString(prepayRequest));try {PrepayWithRequestPaymentResponse response = jsapiService.prepayWithRequestPayment(prepayRequest);log.info("创建预支付单并生成二维码成功,出参:【{}】", JSONObject.toJSONString(response));//未支付监听redisCache.setCacheObject(RedisConstants.ORDER_PAY_RESULT.concat(order.getBatchSn()),JSONObject.toJSONString(response), 10, TimeUnit.MINUTES);redisCache.setCacheObject(RedisConstants.ORDER_NOT_PAY_LISTENER.concat(order.getBatchSn()),NumberUtils.INTEGER_ZERO, 10, TimeUnit.MINUTES);//发送未支付消息redisCache.setCacheObject(RedisConstants.ORDER_MSG_LISTENER.concat(order.getBatchSn()),NumberUtils.INTEGER_ZERO, 2, TimeUnit.MINUTES);return R.ok(response);} catch (HttpException e) { // 发送HTTP请求失败log.error("发送HTTP请求失败:{}", e.getHttpRequest());return R.fail("发送HTTP请求失败");} catch (ServiceException e) { // 服务返回状态小于200或大于等于300,例如500log.error("微信支付返回状态异常,{}", e.getResponseBody());return R.fail("微信支付返回状态异常");} catch (MalformedMessageException e) { // 服务返回成功,返回体类型不合法,或者解析返回体失败log.error("返回类型不合法:{}", e.getMessage());return R.fail("返回类型不合法");}}@Overridepublic void closeOrder(String outTradeNo) {CloseOrderRequest closeOrderRequest = new CloseOrderRequest();closeOrderRequest.setMchid(wechatProperties.getMchId());closeOrderRequest.setOutTradeNo(outTradeNo);jsapiService.closeOrder(closeOrderRequest);log.info("订单关闭成功,入参:【{}】", JSONObject.toJSONString(closeOrderRequest));}@Overridepublic Optional<Transaction> getOrderInfo(String outTradeNo) {QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();request.setOutTradeNo(outTradeNo);request.setMchid(wechatProperties.getMchId());Transaction transaction = jsapiService.queryOrderByOutTradeNo(request);if (Objects.isNull(transaction)) {return Optional.empty();}return Optional.of(transaction);}private String getTimeExpire(){//过期时间  RFC 3339格式SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");//支付订单过期时间return sdf.format(new Date(System.currentTimeMillis() + 1000 * 60 * 5));}}

创建退款单:

@Slf4j
@Service
@EnableConfigurationProperties(WechatProperties.class)
public class WechatRefundServiceImpl implements BaseRefundService {@Resourceprivate RefundService refundService;@Resourceprivate WechatProperties wechatProperties;@Value("${spring.application.name:scm-ofc-system}")private String serviceName;@Resourceprivate OrderDao orderDao;@Resourceprivate OrderItemDao orderItemDao;@Resourceprivate ShopDao shopDao;@Overridepublic R<?> refundOrder(OrderRefund orderRefund) {try {BigDecimal refundAmount = orderRefund.getRefundAmount();Long orderId = orderRefund.getOrderId();Order order = orderDao.queryById(orderId);//退款金额long refundTotal = refundAmount.multiply(BigDecimal.valueOf(100)).longValue();//原支付金额String batchSn = order.getBatchSn();OrderDTO params = new OrderDTO();params.setBatchSn(batchSn);List<Order> orders = orderDao.selectOrderList(params);BigDecimal officialReceipts = orders.stream().map(Order::getPayAmount).reduce(BigDecimal.ZERO, BigDecimal::add);long payTotal = officialReceipts.multiply(BigDecimal.valueOf(100)).longValue();//创建微信退款单CreateRequest createRequest = new CreateRequest();AmountReq amountReq = new AmountReq();amountReq.setCurrency("CNY");amountReq.setTotal(payTotal);amountReq.setRefund(refundTotal);createRequest.setOutTradeNo(batchSn);createRequest.setAmount(amountReq);createRequest.setOutRefundNo(orderRefund.getRefundSn());createRequest.setNotifyUrl(wechatProperties.getDomain() + "/" + serviceName +  "/callback/wechat/refund");log.info("退款单入参:{}", JSONObject.toJSONString(createRequest));Refund refund = refundService.create(createRequest);log.info("创建退款单成功:{}", JSONObject.toJSONString(refund));if (Objects.isNull(refund)) {log.error("退款异常,参数:{}", JSONObject.toJSONString(createRequest));return R.fail(500, "退款异常,请求微信返回值为null:参数" + JSONObject.toJSONString(createRequest));}if (Objects.equals(refund.getStatus(), Status.SUCCESS)) {return R.ok(refund);}} catch (Exception e) {log.error("退款异常:{}", e.getMessage());return R.fail(500, "退款异常,请求微信报错" + e.getMessage());}return R.ok();}}

支付与退款回调

@Slf4j
@Service
public class CallbackServiceImpl implements CallbackService {@Resourceprivate NotificationParser notificationParser;@Resourceprivate RedisCache redisCache;@Resourceprivate OrderService orderService;@Resourceprivate BillDao billDao;@Resourceprivate ShopAccountDao shopAccountDao;@Resourceprivate OrderScmService orderScmService;@Resourceprivate OrderRefundDao orderRefundDao;@Resourceprivate ShopSkuService shopSkuService;@Resourceprivate OrderItemDao orderItemDao;@Resourceprivate MessageService messageService;@Override@Transactional(rollbackFor = Exception.class)public void wechatPayCallback(HttpServletRequest request, HttpServletResponse response) {try {log.info("=========================微信native支付回调通知============================");Transaction transaction = this.verifyAndDecrypt(request, Transaction.class);log.info("验证签名成功:{}", JSONObject.toJSONString(transaction));Transaction.TradeStateEnum tradeState = transaction.getTradeState();if (!Objects.equals(tradeState, Transaction.TradeStateEnum.SUCCESS)) {return;}String outTradeNo = transaction.getOutTradeNo();log.info("支付回调执行成功,待支付->待发货");} catch (Exception e) {log.error("支付回调异常:{}", e.getMessage());}}@Overridepublic void wechatRefundCallback(HttpServletRequest request, HttpServletResponse response) {log.info("=========================微信native退款回调通知============================");RefundNotification refundNotification = this.verifyAndDecrypt(request, RefundNotification.class);Status refundStatus = refundNotification.getRefundStatus();if (!Objects.equals(refundStatus, Status.SUCCESS)) {return;}//执行退款业务}/*** 获取供应链订单id* @param data* @param orderList* @return*/private Map<Long, String> getOrderIdMap(OrderByThirdIdVO data,List<Order> orderList){List<OrderSkuDetailVO> skus = data.getSkus();Map<String, String> skuOrderIdMap = skus.stream().collect(Collectors.toMap(OrderSkuDetailVO::getSku, OrderSkuDetailVO::getOrderId));List<OrderItem> orderItems = orderList.stream().map(Order::getOrderItem).collect(Collectors.toList());return orderItems.stream().map(item -> {String originId = item.getOriginId();String orderId = skuOrderIdMap.get(originId);return new DefaultKeyValue<>(item.getOrderId(), orderId);}).collect(Collectors.toMap(DefaultKeyValue::getKey, DefaultKeyValue::getValue));}/*** 验证并解密报文** @param request*/private <T> T verifyAndDecrypt(HttpServletRequest request, Class<T> clazz) {String timestamp = request.getHeader("Wechatpay-Timestamp");String nonce = request.getHeader("Wechatpay-Nonce");String serialNo = request.getHeader("Wechatpay-Serial");String signature = request.getHeader("Wechatpay-Signature");String signType = request.getHeader("Wechatpay-Signature-Type");String body = this.getBody(request);log.info("\n请求头信息:\n" +"Wechatpay-Timestamp:{},\n" +"Wechatpay-Nonce:{},\n" +"Wechatpay-Serial:{},\n" +"Wechatpay-Signature:{},\n" +"Wechatpay-Signature-Type:{},\n" +"body: {}",timestamp, nonce, serialNo, signature, signType, body);RequestParam requestParam = new RequestParam.Builder().serialNumber(serialNo).nonce(nonce).signature(signature).timestamp(timestamp).signType(signType).body(body).build();return notificationParser.parse(requestParam, clazz);}/*** 获取请求体内容** @param request* @return*/private String getBody(HttpServletRequest request) {BufferedReader reader;String body = "";try {reader = request.getReader();String line;StringBuilder inputString = new StringBuilder();while ((line = reader.readLine()) != null) {inputString.append(line);}body = inputString.toString();} catch (IOException e) {e.printStackTrace();}return body;}}

以上就是微信jsapi支付的对接,仅供参考

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/120455.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

机器学习-学习率:从理论到实战,探索学习率的调整策略

目录 一、引言二、学习率基础定义与解释学习率与梯度下降学习率对模型性能的影响 三、学习率调整策略常量学习率时间衰减自适应学习率AdaGradRMSpropAdam 四、学习率的代码实战环境设置数据和模型常量学习率时间衰减Adam优化器 五、学习率的最佳实践学习率范围测试循环学习率&a…

【spark客户端】Spark SQL CLI详解:怎么执行sql文件、注释怎么写,支持的文件路径协议、交互式模式使用细节

文章目录 一. Spark SQL Command Line Options(命令行参数)二. The hiverc File1. without the -i2. .hiverc 介绍 三. 支持的路径协议四. 支持的注释类型五. Spark SQL CLI交互式命令六. Examples1. running a query from the command line2. setting Hive configuration vari…

缓解光纤激光切割机老化之如何保养光纤激光切割机的光学镜片

激光切割头具备极高的精密度和昂贵的价格&#xff0c;是光纤激光切割机最关键的运行部分之一。在日常的光纤激光切割机维修过程中频繁出现的关于切割头使用寿命的问题就是内部光学镜片的污染及损坏。 部分导致光纤激光切割机激光切割头光学镜片污染的原因主要包括&#xff1a;对…

【APP VTable】和市面上的 Table 组件一样,都是接收表格[] 以及数据源[]

博主&#xff1a;_LJaXi Or 東方幻想郷 专栏&#xff1a; uni-app | 小程序开发 开发工具&#xff1a;HBuilderX 这里写目录标题 表格组件USE 表格组件 <template><view class"scroll-table-wrapper"><view class"scroll-table-container"…

iOS安全加固方法及实现

​ 目录 iOS安全加固方法及实现 摘要 引言 iOS安全加固方法及实现 一、字符串加密 二、类名方法名混淆 三、程序代码混淆 四、加入安全SDK 总结 参考资料 摘要 本文介绍了iOS平台下的应用安全保护方法&#xff0c;包括字符串加密、类名方法名混淆、程序代码混淆和加入…

杂牌行车记录仪特殊AVI结构恢复案例

最近遇到一个杂牌的行车记录仪需要恢复数据&#xff0c;其使用AVI格式&#xff0c;但是在扫描恢复的过程中却发现厂家对其AVI结构进行了“魔改”致程序无法正常识别 故障存储:16G SD卡 fat32文件系统 故障现象: 16G的SD卡&#xff0c;在发生事故后客户尝试自行接到手机上读…

项目进度延误,危机管理5大注意事项

项目延误危机管理的重要性是不可忽视的。项目延误可能会导致资源浪费、成本增加、客户不满、信誉受损等一系列问题&#xff0c;严重影响项目的成功与效益。因此&#xff0c;有效地进行项目延误危机管理是至关重要的&#xff0c;一般主要是从以下5个方面进行管理&#xff1a; 1、…

《动手学深度学习 Pytorch版》 10.6 自注意力和位置编码

在注意力机制中&#xff0c;每个查询都会关注所有的键&#xff0d;值对并生成一个注意力输出。由于查询、键和值来自同一组输入&#xff0c;因此被称为 自注意力&#xff08;self-attention&#xff09;&#xff0c;也被称为内部注意力&#xff08;intra-attention&#xff09;…

竞赛 深度学习人体跌倒检测 -yolo 机器视觉 opencv python

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; **基于深度学习的人体跌倒检测算法研究与实现 ** 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f947;学长这里给一个题目综合评分(每项满…

npm改变npm缓存路径和改变环境变量

在安装nodejs时&#xff0c;系统会自动安装在系统盘C&#xff0c; 时间久了经常会遇到C盘爆满&#xff0c;有时候出现红色&#xff0c;此时才发现很多时候是因为npm 缓存保存在C盘导致的&#xff0c;下面就介绍下如何改变npm缓存路径。 1、首先找到安装nodejs的路径&#xff0c…

JVM(Java Virtual Machine)G1收集器篇

前言 本文参考《深入理解Java虚拟机》&#xff0c;本文主要介绍G1收集器的收集思想和具体过程&#xff08;填上一篇文章留下的坑&#xff09; 本系列其他文章链接&#xff1a; JVM&#xff08;Java Virtual Machine&#xff09;内存模型篇 JVM&#xff08;Java Virtual Machi…

SQL sever中函数(2)

目录 一、函数分类及应用 1.1标量函数&#xff08;Scalar Functions&#xff09;&#xff1a; 1.1.1格式 1.1.2示例 1.1.3作用 1.2表值函数&#xff08;Table-Valued Functions&#xff09;&#xff1a; 1.2.1内联表值函数&#xff08;Inline Table-Valued Functions&am…

Linux shell编程学习笔记15:定义数组、获取数组元素值和长度

一、 Linux shell 脚本编程中的数组概述 数组是一种常见的数据结构。跟大多数编程语言一样&#xff0c;大多数Linux shell脚本支持数组&#xff0c;但对数组的支持程度各不相同&#xff0c;比如数组的维度&#xff0c;是支持一维数组还是多维数组&#xff1f;再如&#xff0c;…

Redis为什么变慢了

一、Redis为什么变慢了 1.Redis真的变慢了吗? 对 Redis 进行基准性能测试 例如,我的机器配置比较低,当延迟为 2ms 时,我就认为 Redis 变慢了,但是如果你的硬件配置比较高,那么在你的运行环境下,可能延迟是 0.5ms 时就可以认为 Redis 变慢了。 所以,你只有了解了你的…

蓝桥杯每日一题2023.10.27

题目描述 快速排序 - 蓝桥云课 (lanqiao.cn) #include <stdio.h>int quick_select(int a[], int l, int r, int k) {int p rand() % (r - l 1) l;int x a[p];{int t a[p]; a[p] a[r]; a[r] t;}int i l, j r;while(i < j) {while(i < j && a[i] &…

Python轮廓追踪【OpenCV形态学操作】

文章目录 概要代码运行结果 概要 一些理论知识 OpenCV形态学操作理论1 OpenCV形态学操作理论2 OpenCV轮廓操作|轮廓类似详解 代码 代码如下&#xff0c;可以直接运行 import cv2 as cv# 定义结构元素 kernel cv.getStructuringElement(cv.MORPH_RECT, (3, 3)) # print kern…

【Linux】rpm和yum的使用

不知道是不是有和我一样的宝子们&#xff0c;在rpm上卡了老久老久&#xff0c;但其实搞通了&#xff0c;理解了原理之后&#xff0c;不难的&#xff0c;所以不管你现在遇到的困难是什么&#xff0c;都不要放弃&#xff0c;一定要坚持&#xff0c;加油。 一、rpm 1.rpm rpm的…

On Moving Object Segmentation from Monocular Video with Transformers 论文阅读

论文信息 标题&#xff1a;On Moving Object Segmentation from Monocular Video with Transformers 作者&#xff1a; 来源&#xff1a;ICCV 时间&#xff1a;2023 代码地址&#xff1a;暂无 Abstract 通过单个移动摄像机进行移动对象检测和分割是一项具有挑战性的任务&am…

使用vue-cli搭建spa项目,vue项目结构说明,开发示例,如何修改端口号

目录 1. vue-cli安装 1.1 安装前提 1.2 什么是vue-cli 1.3 安装vue-cli 2. 使用vue-cli构建项目 2.1 使用脚手架创建项目骨架 2.2 到新建项目目录&#xff0c;安装需要的模块 2.3 如何修改端口号 2.4 添加element-ui模块 2.5 package.json详解 3. install命令中的-g…