【微信支付-服务商】SpringBoot集成微信服务商支付(多子商户集成)

SpringBoot集成微信服务商支付(多子商户集成)

  • 前言
  • 一、前置工作
    • 1、获取商户平台的xxx核心参数
    • 2、关联对应的小程序(appid)
  • 二、SpringBoot集成微信小程序
    • 1、引入pom依赖
    • 2、yml配置
    • 3、java代码文件
      • 3.1、Properties 配置类
      • 3.2 Configuration 服务类
    • 4、使用示例
      • 4.1、发起支付(生成前端签名数据)
      • 4.2、支付回调解析(支付成功解密数据)
        • 4.2.1 接收实体类
        • 4.2.2处理代码
      • 4.3、订单退款
    • 5、更多接口使用请参考

前言

在网上搜了很多,都没有说如何对接微信支付-服务商。于是参考官方代码示例琢磨了一个版本微信官方api文档网站:https://pay.weixin.qq.com/docs/partner/apis/partner-mini-program-payment/partner-mini-prepay.html官方示例demo:https://github.com/wechatpay-apiv3/wechatpay-java

一、前置工作

1、获取商户平台的xxx核心参数

商户号(merchantId)、秘钥(apiV3Key)、商户证书(apiclient_key.pem)、证书序列号(merchantSerialNumber)
在这里插入图片描述

2、关联对应的小程序(appid)

在这里插入图片描述

二、SpringBoot集成微信小程序

1、引入pom依赖

        <!-- 微信支付 --><dependency><groupId>com.github.wechatpay-apiv3</groupId><artifactId>wechatpay-java</artifactId><version>0.2.12</version></dependency>

2、yml配置


pay:# 所关联小程序idappId: wxaxxxxxxxxxxxxxxxmerchantId: 1xxxxxxxx5merchantSerialNumber: xxxxxxxxxxapiV3Key: xxxxxxxxx#支付成功回调地址notifyUrl: https://xxxxxx # 商户平台私钥证书路径privateKeyPath: /xxxxxx/apiclient_key.pem

商户号(merchantId)、秘钥(apiV3Key)、商户证书(apiclient_key.pem)、证书序列号(merchantSerialNumber)

3、java代码文件

3.1、Properties 配置类


import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;@Data
@ConfigurationProperties(prefix = "pay")
public class WxPayServiceProperties {private String appId;private String merchantId;private String merchantSerialNumber;private String apiV3Key;private String notifyUrl;private String privateKeyPath;}

3.2 Configuration 服务类


import com.alibaba.fastjson.JSONObject;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.util.PemUtil;
import com.wechat.pay.java.service.partnerpayments.jsapi.JsapiService;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.RefundService.Builder;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxRuntimeException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;import javax.annotation.PostConstruct;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.Signature;
import java.util.Base64;/*** @author tm* date 2023/5/15* @version 1.0*/
@Slf4j
@Configuration
@EnableConfigurationProperties(WxPayServiceProperties.class)
public class WxPayServiceConfiguration {private final WxPayServiceProperties properties;public static JsapiService service;private static String privateKeyPath;public static RefundService refundService;@Autowiredpublic WxPayServiceConfiguration(WxPayServiceProperties properties) {this.properties = properties;}@PostConstructpublic void init() {WxPayServiceProperties properties = this.properties;if (properties == null) {throw new WxRuntimeException("添加下相关配置,注意别配错了!");}String path = this.getClass().getResource("/").getPath();privateKeyPath = path + "cert/apiclient_key.pem";if (StringUtils.hasText(properties.getPrivateKeyPath())) {privateKeyPath = properties.getPrivateKeyPath();}log.info("证书地址:{}", privateKeyPath);try {// 初始化商户配置Config config =new RSAAutoCertificateConfig.Builder().merchantId(properties.getMerchantId())// 使用 com.wechat.pay.java.core.util 中的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名.privateKeyFromPath(privateKeyPath).merchantSerialNumber(properties.getMerchantSerialNumber()).apiV3Key(properties.getApiV3Key()).build();service = new JsapiService.Builder().config(config).build();log.info("【微信】支付服务加载完成");refundService = new Builder().config(config).build();log.info("【微信】退款服务加载完成");} catch (Exception e) {e.printStackTrace();}}public static String createSign(JSONObject packageParams) {String sign = "";try {StringBuffer append = new StringBuffer();append.append(packageParams.getString("appId")).append("\n");append.append(packageParams.getString("timeStamp")).append("\n");append.append(packageParams.getString("nonceStr")).append("\n");append.append(packageParams.getString("package")).append("\n");Signature Sign = Signature.getInstance("SHA256withRSA");PrivateKey privateKey = PemUtil.loadPrivateKeyFromPath(privateKeyPath);Sign.initSign(privateKey);Sign.update(append.toString().getBytes(StandardCharsets.UTF_8));byte[] signed = Sign.sign();sign = Base64.getEncoder().encodeToString(signed);} catch (Exception ex) {log.error("生成签名失败");}return sign;}}

4、使用示例

4.1、发起支付(生成前端签名数据)


import com.alibaba.fastjson.JSONObject;
import com.wechat.pay.java.service.partnerpayments.jsapi.model.Amount;
import com.wechat.pay.java.service.partnerpayments.jsapi.model.Payer;
import com.wechat.pay.java.service.partnerpayments.jsapi.model.PrepayRequest;
import com.wechat.pay.java.service.partnerpayments.jsapi.model.PrepayResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;
import java.util.Date;/*** Author tm* Date 2024/7/4* Version 1.0*/
@Slf4j
@Validated
@RestController
@RequestMapping("/wx/pay")
public class WxPayController2 {@Resourceprivate WxPayServiceProperties properties;@RequestMapping("createOrder")@Transactionalpublic Result plantPayOrder(PlantRequest paramRequest) {PrepayRequest payRequest = new PrepayRequest();// 调用request.setXxx(val)设置所需参数,具体参数可见Request定义// 调用接口payRequest.setSpAppid(properties.getAppId());payRequest.setSpMchid(properties.getMerchantId());payRequest.setNotifyUrl(properties.getNotifyUrl());payRequest.setSubMchid();// todo 子商户号payRequest.setDescription("备注");payRequest.setOutTradeNo(DateUtils.formatDateMS());//System.currentTimeMillis() + ""Amount amount = new Amount();amount.setTotal();// todo 支付金额payRequest.setAmount(amount);Payer payer = new Payer();payer.setSpOpenid();// todo 小程序用户appidpayRequest.setPayer(payer);payRequest.setAttach("");// todo 附加数据 可空PrepayResponse prepayResponse = WxPayServiceConfiguration.service.prepay(payRequest);if (!StringUtils.hasText(prepayResponse.getPrepayId())) {return Result.error("下单失败");}JSONObject resultJson = new JSONObject();resultJson.put("appId", properties.getAppId());resultJson.put("timeStamp", (new Date()).getTime() / 1000);resultJson.put("nonceStr", DateUtils.currentTimestamp() + DateUtils.buildRandom(4));resultJson.put("package", "prepay_id=" + prepayResponse.getPrepayId());resultJson.put("signType", "RSA");resultJson.put("paySign", WxPayServiceConfiguration.createSign(resultJson));return Result.ok("下单成功", resultJson);}}

4.2、支付回调解析(支付成功解密数据)

4.2.1 接收实体类
import lombok.Data;@Data
public class WxPayCallBackRequest {private String id;private String create_time;private String resource_type;private String event_type;private String summary;private WxPayCallBackResourceVo resource;}
import lombok.Data;/*** Author tm* Date 2024/7/4* Version 1.0*/
@Data
public class WxPayCallBackResourceVo {private String original_type;private String algorithm;private String ciphertext;private String associated_data;private String nonce;}
4.2.2处理代码

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.wechat.pay.java.service.partnerpayments.nativepay.model.Transaction;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;/*** Author tm* Date 2024/7/4* Version 1.0*/
@Slf4j
@Validated
@RestController
@RequestMapping("/wx/pay")
public class WxPayController2 {@Resourceprivate WxPayServiceProperties properties;@RequestMapping("wxPayCallBack")@Transactionalpublic void wxPayCallBack(@RequestBody WxPayCallBackRequest request, HttpServletResponse response) {log.info("【微信支付回调参数】参数:{}", JSON.toJSONString(request));try {response.getWriter().write("SUCCESS");} catch (IOException e) {e.printStackTrace();}WxPayCallBackResourceVo resource = request.getResource();try {Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");SecretKeySpec key = new SecretKeySpec(properties.getApiV3Key().getBytes(StandardCharsets.UTF_8), "AES");GCMParameterSpec spec = new GCMParameterSpec(128, resource.getNonce().getBytes(StandardCharsets.UTF_8));cipher.init(Cipher.DECRYPT_MODE, key, spec);cipher.updateAAD(resource.getAssociated_data().getBytes(StandardCharsets.UTF_8));String parseString = new String(cipher.doFinal(Base64.getDecoder().decode(resource.getCiphertext())), "UTF-8");Transaction transaction = JSONObject.parseObject(parseString, Transaction.class);log.info("【微信支付回调参数】解析后参数:{}", transaction);} catch (Exception e) {e.printStackTrace();}}}

4.3、订单退款

import com.wechat.pay.java.service.refund.model.AmountReq;
import com.wechat.pay.java.service.refund.model.CreateRequest;
import com.wechat.pay.java.service.refund.model.Refund;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;@Slf4j
@Validated
@RestController
@RequestMapping("/wx/pay")
public class WxPayController2 {@Resourceprivate WxPayServiceProperties properties;@RequestMapping("refund")@Transactionalpublic Result cancelClaimPlan(String claimId) {CreateRequest request = new CreateRequest();request.setSubMchid(); // todo 子商户request.setTransactionId(); // todo 微信支付回调唯一标识request.setOutRefundNo(DateUtils.formatDateMS()); // todo  商户退款单号request.setReason("取消认领,进行退款");AmountReq amountReq = new AmountReq();amountReq.setTotal(); // todo 原订单金额amountReq.setRefund(); // todo 退款金额amountReq.setCurrency("CNY");request.setAmount(amountReq);log.info("【取消】【退款】{}", request);Refund refund = WxPayServiceConfiguration.refundService.create(request);log.info("【取消】【退款结果】{}", refund);return Result.ok("退款成功", null);}}

5、更多接口使用请参考

com.wechat.pay.java.service.partnerpayments.jsapi.JsapiService
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

基于JAVA+SpringBoot+Vue的学生干部管理系统

基于JAVASpringBootVue的学生干部管理系统 前言 ✌全网粉丝20W,csdn特邀作者、博客专家、CSDN[新星计划]导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末附源码下载链接&#x1f345; 哈…

windows打开可选功能窗口的方式(呜呜设置里面找不到可选功能只能这样找了)

打开方式 winR打开运行窗口&#xff0c;输入fodhelper&#xff0c;按下回车键 即可快速打开可选功能窗口

ChemChat——大语言模型与化学的未来,以及整合外部工具和聊天机器人的潜力

概述 论文地址&#xff1a;https://arxiv.org/abs/2309.16235 虽然近年来技术创新和变革日新月异&#xff0c;从根本上改变了我们对生物化学过程的认识&#xff0c;但化学领域仍花费大量时间和金钱–"10 年 "和 “3000 亿”–将新产品推向市场。这是由于实验室实验的…

发现编程的全新境界——明基RD280U显示器使用体验

前言 在大学的四年里&#xff0c;我几乎每天都泡在实验室&#xff0c;盯着电脑屏幕&#xff0c;一行行地码代码。那时&#xff0c;学校提供的显示器是非常基础的款式&#xff0c;功能简单&#xff0c;几乎没有任何特别之处&#xff0c;甚至配置也比较低。那个时候&#xff0c;…

Shader 中的光源

1、Shader 开发中常用的光源属性 Unity当中一共支持四种光源类型&#xff1a; 平行光&#xff08;Directional&#xff09;点光源&#xff08;Point&#xff09;聚光灯&#xff08;Spot&#xff09;面光源&#xff08;Area&#xff09;— 面光源仅在烘焙时有用 不管光源类型到…

通过MCGS在ARMxy边缘计算网关上实现物流自动化

随着电子商务和智能制造的快速发展&#xff0c;物流行业面临着前所未有的挑战与机遇。高效的物流系统不仅可以加快货物周转速度&#xff0c;降低运营成本&#xff0c;还能显著提升客户满意度。 1. ARMxy BL340系列简介 ARMxy BL340系列是针对工业自动化领域设计的一款高性能、…

2024年最新苹果cms升级插件【泛目录专用】

苹果CMS是一款专为视频内容管理而设计的系统&#xff0c;近年来在视频站点搭建中逐渐成为热门选择。其直观的用户界面和灵活的管理功能&#xff0c;使得无论是新手还是专业开发者都能轻松上手。 苹果CMS提供了多种主题和模板&#xff0c;用户可以根据自身需求进行定制&#xf…

北京买新能源车,天津上牌攻略

背景说明 我是在北京买的新能源汽车&#xff08;增程式&#xff09;&#xff0c;因没有摇上北京车牌号&#xff0c;所以计划在天津上牌。前期问了一些代理&#xff0c;要是帮忙弄的话得花500元左右&#xff0c;要是自己搞定的话&#xff0c;大约150元左右&#xff08;130元的车…

计算机毕业设计Spark+Hive旅游景点推荐 旅游推荐系统 景区游客满意度预测与优化 Apriori算法 景区客流量预测 旅游大数据 景点规划

流程&#xff1a; 1.DrissionPage自动化爬虫框架采集旅游数据约10万条存入mysql数据库、.csv文件作为数据集(旅游数据、用户数据、评论数据)&#xff1b; 2.使用pandasnumpy或MapReduce对数据进行数据清洗&#xff0c;生成最终的.csv文件并上传到hdfs(含nlp情感分析)&#xff1…

【每日刷题】Day127

【每日刷题】Day127 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;每日刷题&#x1f34d; &#x1f33c;文章目录&#x1f33c; 1. 349. 两个数组的交集 - 力扣&#xff08;LeetCode&#xff09; 2. LCR 022. 环形链表 II - 力扣&a…

软设9.20

1 已知一个文件中出现的各字符及其对应的频率如下表所示。若采用定长编码&#xff0c;则该文件中字符的码长应为()。若采用Hufman编码&#xff0c;则字符序列“face”的编码应为()。 1.&#xff08;&#xff09; A.2 B.3 C.4 D.5 2.&#xff08;&#xff09; A.110001001101…

工程师 - PFM介绍

在电子电路设计中&#xff0c;PFM&#xff08;Pulse Frequency Modulation&#xff0c;脉冲频率调制&#xff09;是一种调制技术&#xff0c;其主要特点是在负载变化时调整脉冲的频率&#xff0c;而保持脉冲的宽度&#xff08;时间长度&#xff09;相对恒定。与PWM&#xff08;…

详解Vue事件总线的原理与应用:EventBus

Vue 事件总线 - 组件通信的桥梁 引言 在 Vue.js 开发中&#xff0c;组件通信是一个重要的话题。Vue 提供了多种方式来实现不同组件之间的通信&#xff0c;譬如Props、 $emit、Ref实例、Vuex状态管理及事件总线等等&#xff0c;可谓是五花八门&#xff0c;它们之间使用各有优缺…

4款音频转文字在线转换工具帮你解锁新的记录模式。

越来越多的人都知道使用一些工具来将音频直接转换成文字&#xff0c;这样便省去了手动输入的麻烦。而且使用音频进行记录也能够提高工作的效率&#xff0c;像会议记录&#xff0c;课堂教学记录&#xff0c;采访录音等。如果大家有需要将自己的音频转成文字&#xff0c;可以试试…

STM32 使用 CubeMX 实现按键外部中断

目录 问题背景知识参考需要改什么注意尽量不要在中断函数使用 循环函数做延时中断函数中延时方法调试 问题 我想实现按钮触发紧急停止类似功能&#xff0c;需要使用按键中断功能。 背景知识 GPIO 点亮 LED。stm32cubemx hal学习记录&#xff1a;GPIO输入输出。STM32—HAL库 …

[c++进阶(八)]STL容器适配器之queue

1.前言 和stack一样&#xff0c;队列也没有把他放在容器的一栏里面&#xff0c;而是把他放在容器适配器的一栏。这也是因为queue是使用了别人的相关接口&#xff0c;空间然后来封装自己的内容&#xff0c;最后再给上层用户使用。 2.队列 队列的性质就是先进先出&#xff0c;他…

黑科技网址推荐:特殊功能的工具网址

1、【网站】WebRTC—— 点对点网络摄像头实时监控 &#xff1b; 网址&#xff1a;https://webcamera.cc/zh 先连接摄像头&#xff0c;将作为摄像头的设备进入摄像头页面&#xff0c;输入连接ID&#xff0c;点连接。 监控端进入监控页面&#xff0c;填入与摄像头相同的连接ID&…

Java中ArrayList和LinkedList的比较

注&#xff1a;Joshua Bloch 就是 LinkedList 的作者 在Java中&#xff0c;ArrayList和LinkedList都是常用的列表实现类&#xff0c;它们都实现了List接口&#xff0c;但在内部工作原理和性能方面有显著差异。 ArrayList&#xff1a;基于动态数组实现。随着元素的增加&#x…

spark之不同序列化对比

一&#xff0c;spark的rdd的序列话不同介绍 下面是使用不同序列化后的占用资源和数据大小 2&#xff0c;sparksql中序列化的区别 sparksql中使用序列化和不使用差别不大&#xff0c;英文sparksql中默认使用了encode自己实现的序列化方法&#xff0c;加上与不加序列化差别不大…

C++ day03

思维导图 头文件 #ifndef SEQLIST_H #define SEQLIST_Husing datatype int;class seqlist { private:datatype *ptr; // 动态数组指针int size; // 顺序表最大容量int len 0; // 当前长度public:void init(int n); // 初始化顺序表bool empty(); …