2024-03-07 朱宝民 虚拟卡注册接口、充值方法与微信支付整合、获取水卡列表接口优化
21个文件已添加
5 文件已重命名
18个文件已删除
8个文件已修改
| New file |
| | |
| | | package com.dy.pipIrrGlobal.daoSe; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.dy.pipIrrGlobal.pojoSe.SeVcRecharge; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | /** |
| | | * @author ZhuBaoMin |
| | | * @date 2024-03-05 20:21 |
| | | * @LastEditTime 2024-03-05 20:21 |
| | | * @Description |
| | | */ |
| | | |
| | | @Mapper |
| | | public interface SeVcRechargeMapper extends BaseMapper<SeVcRecharge> { |
| | | int deleteByPrimaryKey(Long id); |
| | | |
| | | int insert(SeVcRecharge record); |
| | | |
| | | int insertSelective(SeVcRecharge record); |
| | | |
| | | SeVcRecharge selectByPrimaryKey(Long id); |
| | | |
| | | int updateByPrimaryKeySelective(SeVcRecharge record); |
| | | |
| | | int updateByPrimaryKey(SeVcRecharge record); |
| | | |
| | | /** |
| | | * 根据订单号获取虚拟卡充值对象 |
| | | * @param orderNumber |
| | | * @return |
| | | */ |
| | | SeVcRecharge getVCRechargeByorderNumber(String orderNumber); |
| | | } |
| New file |
| | |
| | | package com.dy.pipIrrGlobal.daoSe; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.dy.pipIrrGlobal.pojoSe.SeVirtualCard; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | /** |
| | | * @author ZhuBaoMin |
| | | * @date 2024-03-05 20:45 |
| | | * @LastEditTime 2024-03-05 20:45 |
| | | * @Description |
| | | */ |
| | | |
| | | @Mapper |
| | | public interface SeVirtualCardMapper extends BaseMapper<SeVirtualCard> { |
| | | int deleteByPrimaryKey(Long id); |
| | | |
| | | int insert(SeVirtualCard record); |
| | | |
| | | int insertSelective(SeVirtualCard record); |
| | | |
| | | SeVirtualCard selectByPrimaryKey(Long id); |
| | | |
| | | int updateByPrimaryKeySelective(SeVirtualCard record); |
| | | |
| | | int updateByPrimaryKey(SeVirtualCard record); |
| | | |
| | | } |
| New file |
| | |
| | | package com.dy.pipIrrGlobal.pojoSe; |
| | | |
| | | import com.alibaba.fastjson2.annotation.JSONField; |
| | | import com.alibaba.fastjson2.writer.ObjectWriterImplToString; |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import com.dy.common.po.BaseEntity; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jakarta.validation.constraints.NotBlank; |
| | | import jakarta.validation.constraints.NotNull; |
| | | import lombok.*; |
| | | import org.hibernate.validator.constraints.Length; |
| | | |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * @author ZhuBaoMin |
| | | * @date 2024-03-05 20:21 |
| | | * @LastEditTime 2024-03-05 20:21 |
| | | * @Description |
| | | */ |
| | | |
| | | @TableName(value="se_vc_recharge", autoResultMap = true) |
| | | @Data |
| | | @Builder |
| | | @ToString |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | @Schema(name = "虚拟卡充值实体") |
| | | public class SeVcRecharge implements BaseEntity { |
| | | public static final long serialVersionUID = 202403052025001L; |
| | | |
| | | /** |
| | | * 主键 |
| | | */ |
| | | @JSONField(serializeUsing= ObjectWriterImplToString.class) |
| | | @TableId(type = IdType.INPUT) |
| | | @Schema(description = "实体id", requiredMode = Schema.RequiredMode.NOT_REQUIRED) |
| | | private Long id; |
| | | |
| | | /** |
| | | * 虚拟卡ID |
| | | */ |
| | | @Schema(description = "虚拟卡ID", requiredMode = Schema.RequiredMode.NOT_REQUIRED) |
| | | @NotNull(message = "虚拟卡ID不能为空") |
| | | private Long vcId; |
| | | |
| | | /** |
| | | * 农户ID |
| | | */ |
| | | @Schema(description = "农户ID", requiredMode = Schema.RequiredMode.NOT_REQUIRED) |
| | | @NotNull(message = "农户ID不能为空") |
| | | private Long clientId; |
| | | |
| | | /** |
| | | * 钱包余额 |
| | | */ |
| | | @Schema(description = "钱包余额", requiredMode = Schema.RequiredMode.NOT_REQUIRED) |
| | | @NotNull(message = "钱包余额不能为空") |
| | | private Double money; |
| | | |
| | | /** |
| | | * 充值后余额 |
| | | */ |
| | | @Schema(description = "充值后余额", requiredMode = Schema.RequiredMode.NOT_REQUIRED) |
| | | @NotNull(message = "充值后余额不能为空") |
| | | private Double afterRecharge; |
| | | |
| | | /** |
| | | * 订单号 |
| | | */ |
| | | @Schema(description = "订单号", requiredMode = Schema.RequiredMode.NOT_REQUIRED) |
| | | @NotBlank(message = "订单号不能为空") |
| | | private String orderNumber; |
| | | |
| | | /** |
| | | * 充值金额 |
| | | */ |
| | | @Schema(description = "充值金额", requiredMode = Schema.RequiredMode.NOT_REQUIRED) |
| | | @NotNull(message = "充值金额不能为空") |
| | | private Integer rechargeAmount; |
| | | |
| | | /** |
| | | * 下单时间 |
| | | */ |
| | | @Schema(description = "下单时间", requiredMode = Schema.RequiredMode.NOT_REQUIRED) |
| | | private Date orderTime; |
| | | |
| | | /** |
| | | * 充值完成时间 |
| | | */ |
| | | @Schema(description = "充值完成时间", requiredMode = Schema.RequiredMode.NOT_REQUIRED) |
| | | private Date rechargeTime; |
| | | |
| | | /** |
| | | * 订单状态;1-未支付,2-已支付 |
| | | */ |
| | | @Schema(description = "订单状态", requiredMode = Schema.RequiredMode.NOT_REQUIRED) |
| | | @Length(message = "订单状态不大于{max},不小于{min}", min = 1, max = 2) |
| | | private Byte orderState; |
| | | } |
| New file |
| | |
| | | package com.dy.pipIrrGlobal.pojoSe; |
| | | |
| | | import com.alibaba.fastjson2.annotation.JSONField; |
| | | import com.alibaba.fastjson2.writer.ObjectWriterImplToString; |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import com.dy.common.po.BaseEntity; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jakarta.validation.constraints.Max; |
| | | import jakarta.validation.constraints.Min; |
| | | import jakarta.validation.constraints.NotNull; |
| | | import lombok.*; |
| | | |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * @author ZhuBaoMin |
| | | * @date 2024-03-05 20:45 |
| | | * @LastEditTime 2024-03-05 20:45 |
| | | * @Description |
| | | */ |
| | | |
| | | @TableName(value="se_virtual_card", autoResultMap = true) |
| | | @Data |
| | | @Builder |
| | | @ToString |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | @Schema(name = "虚拟卡实体") |
| | | public class SeVirtualCard implements BaseEntity { |
| | | public static final long serialVersionUID = 202403052048001L; |
| | | |
| | | /** |
| | | * 主键 |
| | | */ |
| | | @JSONField(serializeUsing= ObjectWriterImplToString.class) |
| | | @TableId(type = IdType.INPUT) |
| | | @Schema(description = "实体id", requiredMode = Schema.RequiredMode.NOT_REQUIRED) |
| | | private Long id; |
| | | |
| | | /** |
| | | * 农户ID |
| | | */ |
| | | @Schema(description = "农户ID", requiredMode = Schema.RequiredMode.NOT_REQUIRED) |
| | | @NotNull(message = "农户ID不能为空") |
| | | private Long clientId; |
| | | |
| | | /** |
| | | * 钱包余额 |
| | | */ |
| | | @Schema(description = "钱包余额", requiredMode = Schema.RequiredMode.NOT_REQUIRED) |
| | | @Min(value = 0, message = "钱包余额不能小于0") |
| | | private Double money; |
| | | |
| | | /** |
| | | * 最后操作;1-开户,2-充值,3-消费,4-申请退款,5-退款审核 |
| | | */ |
| | | @Schema(description = "操作类型", requiredMode = Schema.RequiredMode.NOT_REQUIRED) |
| | | @Min(value = 1, message = "最后操作不能小于1") |
| | | @Max(value = 5, message = "最后操作不能大于5") |
| | | private Byte lastOperate; |
| | | |
| | | /** |
| | | * 最后操作时间 |
| | | */ |
| | | @Schema(description = "最后操作时间", requiredMode = Schema.RequiredMode.NOT_REQUIRED) |
| | | private Date lastOperateTime; |
| | | |
| | | /** |
| | | * 创建时间 |
| | | */ |
| | | @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.NOT_REQUIRED) |
| | | private Date createTime; |
| | | |
| | | } |
| | |
| | | private String cardNum; |
| | | |
| | | @Schema(title = "电话号码") |
| | | private Float phone; |
| | | private String phone; |
| | | |
| | | @Schema(title = "身份证号码") |
| | | private Float idCard; |
| | | private String idCard; |
| | | |
| | | @Schema(title = "水卡状态") |
| | | private Float cardState; |
| | | private Integer cardState; |
| | | |
| | | @Schema(title = "水卡状态名称") |
| | | private String stateName; |
| | |
| | | webPort: 8085 |
| | | actutorPort: 9085 |
| | | idSuffix: 7 |
| | | webchat: |
| | | webPort: 8086 |
| | | actutorPort: 9086 |
| | | idSuffix: 8 |
| | | |
| | | #项目编号 |
| | | #projectCode: |
| | |
| | | WHEN card.state = 3 THEN '已挂失' |
| | | End) AS stateName, |
| | | '农户卡' AS cardType, |
| | | card.money |
| | | FORMAT(card.money, 2) AS money |
| | | FROM se_client_card card |
| | | INNER JOIN se_client cli ON card.clientId = cli.id |
| | | <where> |
| New file |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
| | | <mapper namespace="com.dy.pipIrrGlobal.daoSe.SeVcRechargeMapper"> |
| | | <resultMap id="BaseResultMap" type="com.dy.pipIrrGlobal.pojoSe.SeVcRecharge"> |
| | | <!--@mbg.generated--> |
| | | <!--@Table se_vc_recharge--> |
| | | <id column="id" jdbcType="BIGINT" property="id" /> |
| | | <result column="vc_id" jdbcType="BIGINT" property="vcId" /> |
| | | <result column="client_id" jdbcType="BIGINT" property="clientId" /> |
| | | <result column="money" jdbcType="FLOAT" property="money" /> |
| | | <result column="after_recharge" jdbcType="FLOAT" property="afterRecharge" /> |
| | | <result column="order_number" jdbcType="VARCHAR" property="orderNumber" /> |
| | | <result column="recharge_amount" jdbcType="INTEGER" property="rechargeAmount" /> |
| | | <result column="order_time" jdbcType="TIMESTAMP" property="orderTime" /> |
| | | <result column="recharge_time" jdbcType="TIMESTAMP" property="rechargeTime" /> |
| | | <result column="order_state" jdbcType="TINYINT" property="orderState" /> |
| | | </resultMap> |
| | | <sql id="Base_Column_List"> |
| | | <!--@mbg.generated--> |
| | | id, vc_id, client_id, money, after_recharge, order_number, recharge_amount, order_time, |
| | | recharge_time, order_state |
| | | </sql> |
| | | <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap"> |
| | | <!--@mbg.generated--> |
| | | select |
| | | <include refid="Base_Column_List" /> |
| | | from se_vc_recharge |
| | | where id = #{id,jdbcType=BIGINT} |
| | | </select> |
| | | <delete id="deleteByPrimaryKey" parameterType="java.lang.Long"> |
| | | <!--@mbg.generated--> |
| | | delete from se_vc_recharge |
| | | where id = #{id,jdbcType=BIGINT} |
| | | </delete> |
| | | <insert id="insert" parameterType="com.dy.pipIrrGlobal.pojoSe.SeVcRecharge"> |
| | | <!--@mbg.generated--> |
| | | insert into se_vc_recharge (id, vc_id, client_id, |
| | | money, after_recharge, order_number, |
| | | recharge_amount, order_time, recharge_time, |
| | | order_state) |
| | | values (#{id,jdbcType=BIGINT}, #{vcId,jdbcType=BIGINT}, #{clientId,jdbcType=BIGINT}, |
| | | #{money,jdbcType=FLOAT}, #{afterRecharge,jdbcType=FLOAT}, #{orderNumber,jdbcType=VARCHAR}, |
| | | #{rechargeAmount,jdbcType=INTEGER}, #{orderTime,jdbcType=TIMESTAMP}, #{rechargeTime,jdbcType=TIMESTAMP}, |
| | | #{orderState,jdbcType=TINYINT}) |
| | | </insert> |
| | | <insert id="insertSelective" parameterType="com.dy.pipIrrGlobal.pojoSe.SeVcRecharge"> |
| | | <!--@mbg.generated--> |
| | | insert into se_vc_recharge |
| | | <trim prefix="(" suffix=")" suffixOverrides=","> |
| | | <if test="id != null"> |
| | | id, |
| | | </if> |
| | | <if test="vcId != null"> |
| | | vc_id, |
| | | </if> |
| | | <if test="clientId != null"> |
| | | client_id, |
| | | </if> |
| | | <if test="money != null"> |
| | | money, |
| | | </if> |
| | | <if test="afterRecharge != null"> |
| | | after_recharge, |
| | | </if> |
| | | <if test="orderNumber != null"> |
| | | order_number, |
| | | </if> |
| | | <if test="rechargeAmount != null"> |
| | | recharge_amount, |
| | | </if> |
| | | <if test="orderTime != null"> |
| | | order_time, |
| | | </if> |
| | | <if test="rechargeTime != null"> |
| | | recharge_time, |
| | | </if> |
| | | <if test="orderState != null"> |
| | | order_state, |
| | | </if> |
| | | </trim> |
| | | <trim prefix="values (" suffix=")" suffixOverrides=","> |
| | | <if test="id != null"> |
| | | #{id,jdbcType=BIGINT}, |
| | | </if> |
| | | <if test="vcId != null"> |
| | | #{vcId,jdbcType=BIGINT}, |
| | | </if> |
| | | <if test="clientId != null"> |
| | | #{clientId,jdbcType=BIGINT}, |
| | | </if> |
| | | <if test="money != null"> |
| | | #{money,jdbcType=FLOAT}, |
| | | </if> |
| | | <if test="afterRecharge != null"> |
| | | #{afterRecharge,jdbcType=FLOAT}, |
| | | </if> |
| | | <if test="orderNumber != null"> |
| | | #{orderNumber,jdbcType=VARCHAR}, |
| | | </if> |
| | | <if test="rechargeAmount != null"> |
| | | #{rechargeAmount,jdbcType=INTEGER}, |
| | | </if> |
| | | <if test="orderTime != null"> |
| | | #{orderTime,jdbcType=TIMESTAMP}, |
| | | </if> |
| | | <if test="rechargeTime != null"> |
| | | #{rechargeTime,jdbcType=TIMESTAMP}, |
| | | </if> |
| | | <if test="orderState != null"> |
| | | #{orderState,jdbcType=TINYINT}, |
| | | </if> |
| | | </trim> |
| | | </insert> |
| | | <update id="updateByPrimaryKeySelective" parameterType="com.dy.pipIrrGlobal.pojoSe.SeVcRecharge"> |
| | | <!--@mbg.generated--> |
| | | update se_vc_recharge |
| | | <set> |
| | | <if test="vcId != null"> |
| | | vc_id = #{vcId,jdbcType=BIGINT}, |
| | | </if> |
| | | <if test="clientId != null"> |
| | | client_id = #{clientId,jdbcType=BIGINT}, |
| | | </if> |
| | | <if test="money != null"> |
| | | money = #{money,jdbcType=FLOAT}, |
| | | </if> |
| | | <if test="afterRecharge != null"> |
| | | after_recharge = #{afterRecharge,jdbcType=FLOAT}, |
| | | </if> |
| | | <if test="orderNumber != null"> |
| | | order_number = #{orderNumber,jdbcType=VARCHAR}, |
| | | </if> |
| | | <if test="rechargeAmount != null"> |
| | | recharge_amount = #{rechargeAmount,jdbcType=INTEGER}, |
| | | </if> |
| | | <if test="orderTime != null"> |
| | | order_time = #{orderTime,jdbcType=TIMESTAMP}, |
| | | </if> |
| | | <if test="rechargeTime != null"> |
| | | recharge_time = #{rechargeTime,jdbcType=TIMESTAMP}, |
| | | </if> |
| | | <if test="orderState != null"> |
| | | order_state = #{orderState,jdbcType=TINYINT}, |
| | | </if> |
| | | </set> |
| | | where id = #{id,jdbcType=BIGINT} |
| | | </update> |
| | | <update id="updateByPrimaryKey" parameterType="com.dy.pipIrrGlobal.pojoSe.SeVcRecharge"> |
| | | <!--@mbg.generated--> |
| | | update se_vc_recharge |
| | | set vc_id = #{vcId,jdbcType=BIGINT}, |
| | | client_id = #{clientId,jdbcType=BIGINT}, |
| | | money = #{money,jdbcType=FLOAT}, |
| | | after_recharge = #{afterRecharge,jdbcType=FLOAT}, |
| | | order_number = #{orderNumber,jdbcType=VARCHAR}, |
| | | recharge_amount = #{rechargeAmount,jdbcType=INTEGER}, |
| | | order_time = #{orderTime,jdbcType=TIMESTAMP}, |
| | | recharge_time = #{rechargeTime,jdbcType=TIMESTAMP}, |
| | | order_state = #{orderState,jdbcType=TINYINT} |
| | | where id = #{id,jdbcType=BIGINT} |
| | | </update> |
| | | |
| | | <!--根据订单号获取虚拟卡充值对象--> |
| | | <select id="getVCRechargeByorderNumber" resultMap="BaseResultMap"> |
| | | SELECT |
| | | <include refid="Base_Column_List" /> |
| | | FROM se_vc_recharge |
| | | WHERE order_number = #{orderNumber} |
| | | LIMIT 0,1 |
| | | </select> |
| | | </mapper> |
| New file |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
| | | <mapper namespace="com.dy.pipIrrGlobal.daoSe.SeVirtualCardMapper"> |
| | | <resultMap id="BaseResultMap" type="com.dy.pipIrrGlobal.pojoSe.SeVirtualCard"> |
| | | <!--@mbg.generated--> |
| | | <!--@Table se_virtual_card--> |
| | | <id column="id" jdbcType="BIGINT" property="id" /> |
| | | <result column="client_id" jdbcType="BIGINT" property="clientId" /> |
| | | <result column="money" jdbcType="FLOAT" property="money" /> |
| | | <result column="last_operate" jdbcType="TINYINT" property="lastOperate" /> |
| | | <result column="last_operate_time" jdbcType="TIMESTAMP" property="lastOperateTime" /> |
| | | <result column="create_time" jdbcType="TIMESTAMP" property="createTime" /> |
| | | </resultMap> |
| | | <sql id="Base_Column_List"> |
| | | <!--@mbg.generated--> |
| | | id, client_id, money, last_operate, last_operate_time, create_time |
| | | </sql> |
| | | <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap"> |
| | | <!--@mbg.generated--> |
| | | select |
| | | <include refid="Base_Column_List" /> |
| | | from se_virtual_card |
| | | where id = #{id,jdbcType=BIGINT} |
| | | </select> |
| | | <delete id="deleteByPrimaryKey" parameterType="java.lang.Long"> |
| | | <!--@mbg.generated--> |
| | | delete from se_virtual_card |
| | | where id = #{id,jdbcType=BIGINT} |
| | | </delete> |
| | | <insert id="insert" parameterType="com.dy.pipIrrGlobal.pojoSe.SeVirtualCard"> |
| | | <!--@mbg.generated--> |
| | | insert into se_virtual_card (id, client_id, money, |
| | | last_operate, last_operate_time, create_time |
| | | ) |
| | | values (#{id,jdbcType=BIGINT}, #{clientId,jdbcType=BIGINT}, #{money,jdbcType=FLOAT}, |
| | | #{lastOperate,jdbcType=TINYINT}, #{lastOperateTime,jdbcType=TIMESTAMP}, #{createTime,jdbcType=TIMESTAMP} |
| | | ) |
| | | </insert> |
| | | <insert id="insertSelective" parameterType="com.dy.pipIrrGlobal.pojoSe.SeVirtualCard"> |
| | | <!--@mbg.generated--> |
| | | insert into se_virtual_card |
| | | <trim prefix="(" suffix=")" suffixOverrides=","> |
| | | <if test="id != null"> |
| | | id, |
| | | </if> |
| | | <if test="clientId != null"> |
| | | client_id, |
| | | </if> |
| | | <if test="money != null"> |
| | | money, |
| | | </if> |
| | | <if test="lastOperate != null"> |
| | | last_operate, |
| | | </if> |
| | | <if test="lastOperateTime != null"> |
| | | last_operate_time, |
| | | </if> |
| | | <if test="createTime != null"> |
| | | create_time, |
| | | </if> |
| | | </trim> |
| | | <trim prefix="values (" suffix=")" suffixOverrides=","> |
| | | <if test="id != null"> |
| | | #{id,jdbcType=BIGINT}, |
| | | </if> |
| | | <if test="clientId != null"> |
| | | #{clientId,jdbcType=BIGINT}, |
| | | </if> |
| | | <if test="money != null"> |
| | | #{money,jdbcType=FLOAT}, |
| | | </if> |
| | | <if test="lastOperate != null"> |
| | | #{lastOperate,jdbcType=TINYINT}, |
| | | </if> |
| | | <if test="lastOperateTime != null"> |
| | | #{lastOperateTime,jdbcType=TIMESTAMP}, |
| | | </if> |
| | | <if test="createTime != null"> |
| | | #{createTime,jdbcType=TIMESTAMP}, |
| | | </if> |
| | | </trim> |
| | | </insert> |
| | | <update id="updateByPrimaryKeySelective" parameterType="com.dy.pipIrrGlobal.pojoSe.SeVirtualCard"> |
| | | <!--@mbg.generated--> |
| | | update se_virtual_card |
| | | <set> |
| | | <if test="clientId != null"> |
| | | client_id = #{clientId,jdbcType=BIGINT}, |
| | | </if> |
| | | <if test="money != null"> |
| | | money = #{money,jdbcType=FLOAT}, |
| | | </if> |
| | | <if test="lastOperate != null"> |
| | | last_operate = #{lastOperate,jdbcType=TINYINT}, |
| | | </if> |
| | | <if test="lastOperateTime != null"> |
| | | last_operate_time = #{lastOperateTime,jdbcType=TIMESTAMP}, |
| | | </if> |
| | | <if test="createTime != null"> |
| | | create_time = #{createTime,jdbcType=TIMESTAMP}, |
| | | </if> |
| | | </set> |
| | | where id = #{id,jdbcType=BIGINT} |
| | | </update> |
| | | <update id="updateByPrimaryKey" parameterType="com.dy.pipIrrGlobal.pojoSe.SeVirtualCard"> |
| | | <!--@mbg.generated--> |
| | | update se_virtual_card |
| | | set client_id = #{clientId,jdbcType=BIGINT}, |
| | | money = #{money,jdbcType=FLOAT}, |
| | | last_operate = #{lastOperate,jdbcType=TINYINT}, |
| | | last_operate_time = #{lastOperateTime,jdbcType=TIMESTAMP}, |
| | | create_time = #{createTime,jdbcType=TIMESTAMP} |
| | | where id = #{id,jdbcType=BIGINT} |
| | | </update> |
| | | |
| | | </mapper> |
| | |
| | | <name>pipIrr-web-sell</name> |
| | | <description>web营销信息系统</description> |
| | | |
| | | <dependencies> |
| | | <!--OkHttp--> |
| | | <dependency> |
| | | <groupId>com.squareup.okhttp3</groupId> |
| | | <artifactId>okhttp</artifactId> |
| | | <version>4.9.2</version> |
| | | </dependency> |
| | | </dependencies> |
| | | |
| | | <build> |
| | | <plugins> |
| | | <!-- 生成不包含依赖jar的可执行jar包 |
| File was renamed from pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/config/RestTemplateConfig.java |
| | |
| | | package com.dy.pipirrWebChat.config; |
| | | package com.dy.pipIrrSell.config; |
| | | |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.context.annotation.Bean; |
| | |
| | | |
| | | import java.nio.charset.StandardCharsets; |
| | | import java.util.List; |
| | | //import org.apache.http.client.HttpClient; |
| | | |
| | | /** |
| | | * @author ZhuBaoMin |
| | | * @date 2024-02-23 15:42 |
| | | * @LastEditTime 2024-02-23 15:42 |
| | | * @date 2024-03-06 11:43 |
| | | * @LastEditTime 2024-03-06 11:43 |
| | | * @Description |
| | | */ |
| | | |
| | |
| | | return restTemplate; |
| | | } |
| | | } |
| | | |
| File was renamed from pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/config/RestTemplateWechatCertConfig.java |
| | |
| | | package com.dy.pipirrWebChat.config; |
| | | package com.dy.pipIrrSell.config; |
| | | |
| | | import com.dy.pipirrWebChat.payment.PayInfo; |
| | | import com.dy.pipIrrSell.wechatpay.PayInfo; |
| | | import okhttp3.OkHttpClient; |
| | | import org.springframework.boot.context.properties.ConfigurationProperties; |
| | | import org.springframework.context.annotation.Bean; |
| | |
| | | |
| | | /** |
| | | * @author ZhuBaoMin |
| | | * @date 2024-02-23 19:18 |
| | | * @LastEditTime 2024-02-23 19:18 |
| | | * @date 2024-03-06 11:44 |
| | | * @LastEditTime 2024-03-06 11:44 |
| | | * @Description |
| | | */ |
| | | |
| | |
| | | AUDITS_ADD_FAIL(80001, "总账审核记录添加失败"), |
| | | |
| | | /** |
| | | * 电子钱包 |
| | | * 虚拟卡 |
| | | */ |
| | | |
| | | VERIFY_FAIL(10001, "验签失败"), |
| | | |
| | | TOTAL_REFUND_EXCEED_TRADE(10001, "退款总额超过订单金额"), |
| | | ACCEPTED(10001, "退款申请已受理"), |
| | | PROCESSING(10001, "退款处理中"), |
| | | ABNORMAL(10001, "退款异常"), |
| | | RECHARGE_ADD_FAIL(10001, "充值记录添加失败"), |
| | | |
| | | CLIENT_ID_CANNOT_BE_NULL(90001, "农户编号不能为空"), |
| | | WALLET_OPEN_ACCOUNT_FAIL(90002, "电子钱包账户注册失败"), |
| | | WALLET_ACCOUNT_EXIST(90003, "该农户已注册电子钱包"), |
| | | NO_ACCOUNT(90004, "您尚未注册电子钱包账户"), |
| | | WALLET_OPEN_ACCOUNT_FAIL(90002, "虚拟卡账户注册失败"), |
| | | //WALLET_ACCOUNT_EXIST(90003, "该农户已注册电子钱包"), |
| | | NO_ACCOUNT(90004, "您指定的虚拟卡未注册"), |
| | | |
| | | UPDATE_ACCOUNT_FAIL(90005, "充值失败,电子钱包账户更新失败"), |
| | | RECHARGE_FAIL(90006, "充值失败"), |
| | | RECHARGE_NOT_EXIST(90006, "充值记录不存在"), |
| | | VIRTUAL_CARD_NOT_EXIST(90006, "虚拟卡账户不存在"), |
| | | |
| | | BALANCE_IS_INSUFFICIENT(90007, "消费失败,余额不足"), |
| | | CONSUME_FAIL(90008, "消费失败"), |
| | | REFUND_AMOUNT_CANNOT_GREATER_THAN_MONEY(90009, "申请退款失败,退款金额不能大于余额"), |
| New file |
| | |
| | | package com.dy.pipIrrSell.util; |
| | | |
| | | import javax.crypto.Cipher; |
| | | import javax.crypto.NoSuchPaddingException; |
| | | import javax.crypto.spec.GCMParameterSpec; |
| | | import javax.crypto.spec.SecretKeySpec; |
| | | import java.io.IOException; |
| | | import java.security.GeneralSecurityException; |
| | | import java.security.InvalidAlgorithmParameterException; |
| | | import java.security.InvalidKeyException; |
| | | import java.security.NoSuchAlgorithmException; |
| | | import java.util.Base64; |
| | | |
| | | /** |
| | | * @author ZhuBaoMin |
| | | * @date 2024-03-06 11:46 |
| | | * @LastEditTime 2024-03-06 11:46 |
| | | * @Description |
| | | */ |
| | | |
| | | public class AesUtil { |
| | | static final int KEY_LENGTH_BYTE = 32; |
| | | static final int TAG_LENGTH_BIT = 128; |
| | | |
| | | /** |
| | | * 解密 |
| | | * @param apiV3Key apiV3密钥 |
| | | * @param associatedData 附加数据 |
| | | * @param nonce 随机串 |
| | | * @param ciphertext 数据密文 |
| | | * @return 解密后字符串 |
| | | * @throws GeneralSecurityException |
| | | * @throws IOException |
| | | */ |
| | | public static String decryptToString(byte[] apiV3Key, byte[] associatedData, byte[] nonce, String ciphertext) throws GeneralSecurityException, IOException { |
| | | try { |
| | | Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); |
| | | SecretKeySpec key = new SecretKeySpec(apiV3Key, "AES"); |
| | | GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce); |
| | | |
| | | cipher.init(Cipher.DECRYPT_MODE, key, spec); |
| | | cipher.updateAAD(associatedData); |
| | | |
| | | return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), "utf-8"); |
| | | } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { |
| | | throw new IllegalStateException(e); |
| | | } catch (InvalidKeyException | InvalidAlgorithmParameterException e) { |
| | | throw new IllegalArgumentException(e); |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dy.pipIrrSell.util; |
| | | |
| | | import javax.crypto.Mac; |
| | | import javax.crypto.spec.SecretKeySpec; |
| | | import java.nio.charset.StandardCharsets; |
| | | import java.security.InvalidKeyException; |
| | | import java.security.NoSuchAlgorithmException; |
| | | |
| | | /** |
| | | * @author ZhuBaoMin |
| | | * @date 2024-03-06 11:47 |
| | | * @LastEditTime 2024-03-06 11:47 |
| | | * @Description |
| | | */ |
| | | public class HmacSha256 { |
| | | public static String getSignature(String secretKey, String data) throws NoSuchAlgorithmException, InvalidKeyException { |
| | | // 创建密钥对象 |
| | | byte[] keyBytes = secretKey.getBytes(StandardCharsets.UTF_8); |
| | | SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "HmacSHA256"); |
| | | |
| | | // 创建Mac对象并初始化 |
| | | Mac mac = Mac.getInstance("HmacSHA256"); |
| | | mac.init(secretKeySpec); |
| | | |
| | | // 将待加密的数据转换为字节数组 |
| | | byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8); |
| | | |
| | | // 使用密钥对数据进行加密 |
| | | byte[] encryptedBytes = mac.doFinal(dataBytes); |
| | | |
| | | // 将加密后的结果转换为十六进制字符串 |
| | | StringBuilder hexString = new StringBuilder(); |
| | | for (byte b : encryptedBytes) { |
| | | String hex = Integer.toHexString(0xff & b); |
| | | if (hex.length() == 1) { |
| | | hexString.append('0'); |
| | | } |
| | | hexString.append(hex); |
| | | } |
| | | String hmacSha256 = hexString.toString(); |
| | | return hmacSha256; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dy.pipIrrSell.util; |
| | | |
| | | import com.alibaba.fastjson2.JSONArray; |
| | | import com.alibaba.fastjson2.JSONObject; |
| | | |
| | | import com.dy.pipIrrSell.wechatpay.PayInfo; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import javax.crypto.NoSuchPaddingException; |
| | | import java.io.ByteArrayInputStream; |
| | | import java.io.IOException; |
| | | import java.nio.charset.StandardCharsets; |
| | | import java.nio.file.Files; |
| | | import java.nio.file.Paths; |
| | | import java.security.*; |
| | | import java.security.cert.Certificate; |
| | | import java.security.cert.CertificateException; |
| | | import java.security.cert.CertificateFactory; |
| | | import java.security.spec.InvalidKeySpecException; |
| | | import java.security.spec.PKCS8EncodedKeySpec; |
| | | import java.util.Base64; |
| | | import java.util.HashMap; |
| | | import java.util.Map; |
| | | import java.util.Random; |
| | | |
| | | /** |
| | | * @author ZhuBaoMin |
| | | * @date 2024-03-06 11:47 |
| | | * @LastEditTime 2024-03-06 11:47 |
| | | * @Description |
| | | */ |
| | | @Component |
| | | @RequiredArgsConstructor |
| | | public class PayHelper { |
| | | private final RestTemplateUtil restTemplateUtil; |
| | | |
| | | private static final String CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; |
| | | |
| | | private String checkSessionUrl = PayInfo.checkSessionUrl; |
| | | private String tokenUrl = PayInfo.tokenUrl; |
| | | private String resetUserSessionKeyUrl = PayInfo.resetUserSessionKeyUrl; |
| | | |
| | | // 平台证书公钥 |
| | | public Map<String, Certificate> CERTIFICATE_MAP = new HashMap(); |
| | | |
| | | /** |
| | | * 获取32位随机字符串 |
| | | * @return 随机串 |
| | | */ |
| | | public String generateRandomString() { |
| | | Random random = new Random(); |
| | | StringBuilder sb = new StringBuilder(32); |
| | | for (int i = 0; i < 32; i++) { |
| | | int index = random.nextInt(CHARACTERS.length()); |
| | | sb.append(CHARACTERS.charAt(index)); |
| | | } |
| | | return sb.toString(); |
| | | } |
| | | |
| | | /** |
| | | * 获取商户证书私钥对象 |
| | | * @param filename 私钥文件路径 |
| | | * @return 私钥对象 |
| | | * @throws IOException |
| | | */ |
| | | public PrivateKey getPrivateKey(String filename) throws IOException { |
| | | String content = new String(Files.readAllBytes(Paths.get(filename)), "utf-8"); |
| | | try { |
| | | String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "") |
| | | .replace("-----END PRIVATE KEY-----", "") |
| | | .replaceAll("\\s+", ""); |
| | | KeyFactory kf = KeyFactory.getInstance("RSA"); |
| | | return kf.generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey))); |
| | | } catch (NoSuchAlgorithmException e) { |
| | | throw new RuntimeException("当前Java环境不支持RSA", e); |
| | | } catch (InvalidKeySpecException e) { |
| | | throw new RuntimeException("无效的密钥格式"); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 检验登录态 |
| | | * @param appid 小程序 appId |
| | | * @param secret 小程序 appSecret |
| | | * @param openid 用户唯一标识符 |
| | | * @param sessionKey 会话密钥 |
| | | * @return |
| | | * @throws IOException |
| | | */ |
| | | public JSONObject checkSessionKey(String appid, String secret, String openid, String sessionKey) throws IOException, NoSuchAlgorithmException, InvalidKeyException { |
| | | String accessToken = ""; |
| | | Integer expiresIn = 0; |
| | | String signature = HmacSha256.getSignature(sessionKey, ""); |
| | | String sigMethod = "hmac_sha256"; |
| | | |
| | | JSONObject job_token = getAccessToken(appid, secret); |
| | | if(job_token != null) { |
| | | accessToken = job_token.getString("access_token"); |
| | | expiresIn = job_token.getInteger("expires_in"); |
| | | } |
| | | |
| | | Map<String, Object> queryParams = new HashMap<>(); |
| | | queryParams.put("access_token", accessToken); |
| | | queryParams.put("openid", openid); |
| | | queryParams.put("signature", signature); |
| | | queryParams.put("sig_method", sigMethod); |
| | | Map<String, String> headerParams = new HashMap<>(); |
| | | JSONObject result = restTemplateUtil.get(checkSessionUrl, queryParams, headerParams); |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * 重置登录态 |
| | | * @param appid 小程序 appId |
| | | * @param secret 小程序 appSecret |
| | | * @param openid 用户唯一标识符 |
| | | * @param sessionKey 会话密钥 |
| | | * @return |
| | | * @throws NoSuchAlgorithmException |
| | | * @throws InvalidKeyException |
| | | * @throws IOException |
| | | */ |
| | | public JSONObject resetUserSessionKey(String appid, String secret, String openid, String sessionKey) throws NoSuchAlgorithmException, InvalidKeyException, IOException { |
| | | String accessToken = ""; |
| | | Integer expiresIn = 0; |
| | | String signature = HmacSha256.getSignature(sessionKey, ""); |
| | | String sigMethod = "hmac_sha256"; |
| | | |
| | | JSONObject job_token = getAccessToken(appid, secret); |
| | | if(job_token != null) { |
| | | accessToken = job_token.getString("access_token"); |
| | | expiresIn = job_token.getInteger("expires_in"); |
| | | } |
| | | |
| | | Map<String, Object> queryParams = new HashMap<>(); |
| | | queryParams.put("access_token", accessToken); |
| | | queryParams.put("openid", openid); |
| | | queryParams.put("signature", signature); |
| | | queryParams.put("sig_method", sigMethod); |
| | | Map<String, String> headerParams = new HashMap<>(); |
| | | JSONObject result = restTemplateUtil.get(resetUserSessionKeyUrl, queryParams, headerParams); |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * 获取接口调用凭据 |
| | | * @param appid 小程序 appId |
| | | * @param secret 小程序 appSecret |
| | | * @return 凭据及凭据有效时间 |
| | | * @throws IOException |
| | | */ |
| | | public JSONObject getAccessToken(String appid, String secret) throws IOException { |
| | | Map<String, Object> queryParams = new HashMap<>(); |
| | | queryParams.put("grant_type", "client_credential"); |
| | | queryParams.put("appid", appid); |
| | | queryParams.put("secret", secret); |
| | | Map<String, String> headerParams = new HashMap<>(); |
| | | JSONObject job_result = restTemplateUtil.get(tokenUrl, queryParams, headerParams); |
| | | return job_result; |
| | | } |
| | | |
| | | /** |
| | | * 构造签名串_下单 |
| | | * @param method HTTP请求方法 |
| | | * @param url URL |
| | | * @param timestamp 时间戳 |
| | | * @param nonceStr 随机串 |
| | | * @param body 报文主题 |
| | | * @return 签名串 |
| | | */ |
| | | public String buildMessage_order(String method, String url, long timestamp, String nonceStr, String body) { |
| | | return method + "\n" |
| | | + url + "\n" |
| | | + timestamp + "\n" |
| | | + nonceStr + "\n" |
| | | + body + "\n"; |
| | | } |
| | | |
| | | /** |
| | | * 构造签名串_再次下单 |
| | | * @param appid 小程序唯一标识 |
| | | * @param timestamp 时间戳 |
| | | * @param nonceStr 随机串 |
| | | * @param pkg package |
| | | * @return 签名串 |
| | | */ |
| | | public String buildMessage_signAgain(String appid, String timestamp, String nonceStr, String pkg) { |
| | | return appid + "\n" |
| | | + timestamp + "\n" |
| | | + nonceStr + "\n" |
| | | + pkg + "\n"; |
| | | } |
| | | |
| | | /** |
| | | * 签名 |
| | | * @param message 被签名信息 |
| | | * @param certFileName 私钥证书文件路径 |
| | | * @return signature签名值,签名信息中的一项,参与生成签名信息 |
| | | * @throws NoSuchAlgorithmException |
| | | * @throws InvalidKeyException |
| | | * @throws SignatureException |
| | | * @throws IOException |
| | | */ |
| | | public String sign(byte[] message, String certFileName) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, IOException { |
| | | Signature sign = Signature.getInstance("SHA256withRSA"); |
| | | sign.initSign(getPrivateKey(certFileName)); |
| | | sign.update(message); |
| | | return Base64.getEncoder().encodeToString(sign.sign()); |
| | | } |
| | | |
| | | /** |
| | | * 获取签名信息 |
| | | * @param method |
| | | * @param url |
| | | * @param body |
| | | * @return 签名信息,HTTP头中的签名信息 |
| | | * HTTP头:Authorization: 认证类型 签名信息 |
| | | * 认证类型,WECHATPAY2-SHA256-RSA2048 |
| | | */ |
| | | public String getToken(String method, String url, String body, String nonceStr, Long timestamp, String certFileName) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException, SignatureException, InvalidKeyException, NoSuchPaddingException { |
| | | String message = buildMessage_order(method, url, timestamp, nonceStr, body); |
| | | String signature = sign(message.getBytes("utf-8"), certFileName); |
| | | |
| | | return "mchid=\"" + PayInfo.mchid + "\"," |
| | | + "nonce_str=\"" + nonceStr + "\"," |
| | | + "timestamp=\"" + timestamp + "\"," |
| | | + "serial_no=\"" + PayInfo.serial_no + "\"," |
| | | + "signature=\"" + signature + "\""; |
| | | } |
| | | |
| | | /** |
| | | * 构造验造签名串 |
| | | * @param wechatpayTimestamp 请求头中返回的时间戳 |
| | | * @param wechatpayNonce 请求头中返回的随机串 |
| | | * @param boey 请求返回的body |
| | | * @return signatureStr构造的验签名串 |
| | | */ |
| | | public String responseSign(String wechatpayTimestamp, String wechatpayNonce, String boey) { |
| | | String signatureStr = wechatpayTimestamp + "\n" |
| | | + wechatpayNonce + "\n" |
| | | + boey + "\n"; |
| | | return signatureStr; |
| | | } |
| | | |
| | | /** |
| | | * 重新下载证书 |
| | | */ |
| | | public void refreshCertificate() throws GeneralSecurityException, IOException { |
| | | String method = "GET"; |
| | | String httpUrl = "/v3/certificates"; |
| | | String nonceStr = generateRandomString(); |
| | | Long timestamp = System.currentTimeMillis() / 1000; |
| | | |
| | | String header = PayInfo.schema + " " + getToken(method, httpUrl, "", nonceStr, timestamp, PayInfo.privateCertFileName); |
| | | |
| | | Map<String, String> headers = new HashMap<>(); |
| | | headers.put("Authorization", header); |
| | | headers.put("Accept", "application/json"); |
| | | |
| | | JSONObject job_result = restTemplateUtil.getHeaders(PayInfo.certificates,null, headers); |
| | | JSONObject job_headers = job_result.getJSONObject("headers"); |
| | | |
| | | String wechatpayNonce = job_headers.getJSONArray("Wechatpay-Nonce").getString(0); |
| | | String wechatpaySerial = job_headers.getJSONArray("Wechatpay-Serial").getString(0); |
| | | String signature_h = job_headers.getJSONArray("Wechatpay-Signature").getString(0); |
| | | String signatureType_h = job_headers.getJSONArray("Wechatpay-Signature-Type").getString(0); |
| | | String wechatpayTimestamp = job_headers.getJSONArray("Wechatpay-Timestamp").getString(0); |
| | | |
| | | JSONObject job_body = job_result.getJSONObject("body"); |
| | | if(job_body != null) { |
| | | JSONArray array = job_body.getJSONArray("data"); |
| | | if(array != null && array.size() > 0) { |
| | | for(int i = 0; i < array.size(); i++) { |
| | | JSONObject job_data = array.getJSONObject(i); |
| | | String certificateSerial = job_data.getString("serial_no"); |
| | | String effective_time = job_data.getString("effective_time"); |
| | | String expire_time = job_data.getString("expire_time"); |
| | | JSONObject job_certificate = job_data.getJSONObject("encrypt_certificate"); |
| | | String algorithm = job_certificate.getString("algorithm"); |
| | | String nonce = job_certificate.getString("nonce"); |
| | | String associated_data = job_certificate.getString("associated_data"); |
| | | String ciphertext = job_certificate.getString("ciphertext"); |
| | | |
| | | //对证书密文进行解密得到平台证书公钥 |
| | | String publicKey = AesUtil.decryptToString(PayInfo.key.getBytes("utf-8"), associated_data.getBytes("utf-8"), nonce.getBytes("utf-8"), ciphertext); |
| | | |
| | | // 将平台公钥字符串转成Certificate对象 |
| | | final CertificateFactory cf = CertificateFactory.getInstance("X509"); |
| | | ByteArrayInputStream inputStream = new ByteArrayInputStream(publicKey.getBytes(StandardCharsets.UTF_8)); |
| | | Certificate certificate = null; |
| | | try { |
| | | certificate = cf.generateCertificate(inputStream); |
| | | } catch (CertificateException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | |
| | | // 响应头证书序号与响应体证书序列号一致,且时间差小于5分钟时才将证书存储map |
| | | Long timeDiff = (System.currentTimeMillis() / 1000 - Long.parseLong(wechatpayTimestamp))/60; |
| | | if(wechatpaySerial.equals(certificateSerial) && timeDiff <= 5) { |
| | | // 证书放入MAP |
| | | CERTIFICATE_MAP.put(certificateSerial, certificate); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 使用微信平台证书进行响应验签 |
| | | * @param wechatpaySerial 来自响应头的微信平台证书序列号 |
| | | * @param signatureStr 构造的验签名串 |
| | | * @param wechatpaySignature 来自响应头的微信平台签名 |
| | | * @return |
| | | * @throws NoSuchAlgorithmException |
| | | * @throws InvalidKeyException |
| | | * @throws SignatureException |
| | | */ |
| | | public Boolean responseSignVerify(String wechatpaySerial, String signatureStr, String wechatpaySignature) throws GeneralSecurityException, IOException { |
| | | if(CERTIFICATE_MAP.isEmpty() || !CERTIFICATE_MAP.containsKey(wechatpaySerial)) { |
| | | CERTIFICATE_MAP.clear(); |
| | | refreshCertificate(); |
| | | } |
| | | Certificate certificate = (Certificate)CERTIFICATE_MAP.get(wechatpaySerial); |
| | | if(certificate == null) { |
| | | return false; |
| | | } |
| | | |
| | | // 获取公钥 |
| | | PublicKey publicKey = certificate.getPublicKey(); |
| | | |
| | | // 初始化SHA256withRSA前面器 |
| | | Signature signature = Signature.getInstance("SHA256withRSA"); |
| | | // 用微信平台公钥对前面器进行初始化 |
| | | signature.initVerify(certificate); |
| | | |
| | | // 将构造的验签名串更新到签名器中 |
| | | signature.update(signatureStr.getBytes(StandardCharsets.UTF_8)); |
| | | |
| | | // 请求头中微信服务器返回的签名用Base64解码,使用签名器进行验证 |
| | | boolean valid = signature.verify(Base64.getDecoder().decode(wechatpaySignature)); |
| | | return valid; |
| | | } |
| | | } |
| File was renamed from pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/util/RestTemplateUtil.java |
| | |
| | | package com.dy.pipirrWebChat.util; |
| | | package com.dy.pipIrrSell.util; |
| | | |
| | | import com.alibaba.fastjson2.JSONObject; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | |
| | | |
| | | /** |
| | | * @author ZhuBaoMin |
| | | * @date 2024-02-23 15:42 |
| | | * @LastEditTime 2024-02-23 15:42 |
| | | * @date 2024-03-06 13:47 |
| | | * @LastEditTime 2024-03-06 13:47 |
| | | * @Description |
| | | */ |
| | | |
| | |
| | | return JSONObject.parseObject(response.getBody()); |
| | | } |
| | | |
| | | public JSONObject getHeaders(String url, Map<String, Object> queryParams, Map<String, String> headerParams) throws IOException { |
| | | String tempUrl = setParamsByAppendUrl(queryParams, url); |
| | | HttpHeaders headers = new HttpHeaders(); |
| | | headerParams.forEach(headers::add); |
| | | HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<>(null, headers); |
| | | ResponseEntity<String> response = restTemplate.exchange(tempUrl, HttpMethod.GET, httpEntity, String.class); |
| | | |
| | | JSONObject jsonObject = new JSONObject(); |
| | | jsonObject.put("headers", response.getHeaders()); |
| | | jsonObject.put("body", response.getBody()); |
| | | return jsonObject; |
| | | } |
| | | |
| | | public JSONObject get2(String url, Map<String, Object> queryParams, Map<String, String> headerParams) throws IOException { |
| | | String tempUrl = setParamsByPath(queryParams, url); |
| | | HttpHeaders headers = new HttpHeaders(); |
| New file |
| | |
| | | package com.dy.pipIrrSell.virtualCard; |
| | | |
| | | import com.dy.common.aop.SsoAop; |
| | | import com.dy.common.webUtil.BaseResponse; |
| | | import com.dy.common.webUtil.BaseResponseUtils; |
| | | import com.dy.common.webUtil.ResultCodeMsg; |
| | | import com.dy.pipIrrGlobal.pojoSe.SeVirtualCard; |
| | | import com.dy.pipIrrSell.result.SellResultCode; |
| | | import com.dy.pipIrrSell.virtualCard.enums.LastOperateENUM; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.media.Content; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import io.swagger.v3.oas.annotations.responses.ApiResponse; |
| | | import io.swagger.v3.oas.annotations.responses.ApiResponses; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import jakarta.validation.constraints.NotNull; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.http.MediaType; |
| | | import org.springframework.validation.annotation.Validated; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RequestParam; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * @author ZhuBaoMin |
| | | * @date 2024-03-06 8:40 |
| | | * @LastEditTime 2024-03-06 8:40 |
| | | * @Description |
| | | */ |
| | | |
| | | @Slf4j |
| | | @Tag(name = "虚拟卡管理", description = "虚拟卡管理") |
| | | @RestController |
| | | @RequestMapping(path="virtual_card") |
| | | @RequiredArgsConstructor |
| | | @Validated |
| | | public class VirtualCardCtrl { |
| | | private final VirtualCardSv virtualCardSv; |
| | | |
| | | @Operation(summary = "注册虚拟卡", description = "注册虚拟卡") |
| | | @ApiResponses(value = { |
| | | @ApiResponse( |
| | | responseCode = ResultCodeMsg.RsCode.SUCCESS_CODE, |
| | | description = "操作结果:true:成功,false:失败(BaseResponse.content)", |
| | | content = {@Content(mediaType = MediaType.APPLICATION_JSON_VALUE, |
| | | schema = @Schema(implementation = Boolean.class))} |
| | | ) |
| | | }) |
| | | @PostMapping(path = "add_vc") |
| | | @SsoAop() |
| | | public BaseResponse<Boolean> addWallet(@RequestParam("clientId") @NotNull(message = "农户编号不能为空") Long clientId){ |
| | | if(clientId == null || clientId < 0) { |
| | | return BaseResponseUtils.buildFail(SellResultCode.CLIENT_ID_CANNOT_BE_NULL.getMessage()); |
| | | } |
| | | |
| | | SeVirtualCard seVirtualCard = new SeVirtualCard(); |
| | | seVirtualCard.setClientId(clientId); |
| | | seVirtualCard.setMoney(0d); |
| | | seVirtualCard.setLastOperate(LastOperateENUM.OPEN_ACCOUNT.getCode()); |
| | | seVirtualCard.setLastOperateTime(new Date()); |
| | | seVirtualCard.setCreateTime(new Date()); |
| | | Long rec = virtualCardSv.insertVirtualCard(seVirtualCard); |
| | | if(rec == null) { |
| | | return BaseResponseUtils.buildFail(SellResultCode.WALLET_OPEN_ACCOUNT_FAIL.getMessage()); |
| | | } |
| | | return BaseResponseUtils.buildSuccess(true) ; |
| | | } |
| | | |
| | | |
| | | } |
| New file |
| | |
| | | package com.dy.pipIrrSell.virtualCard; |
| | | |
| | | import com.dy.common.webUtil.BaseResponse; |
| | | import com.dy.common.webUtil.BaseResponseUtils; |
| | | import com.dy.pipIrrGlobal.daoSe.SeVcRechargeMapper; |
| | | import com.dy.pipIrrGlobal.daoSe.SeVirtualCardMapper; |
| | | import com.dy.pipIrrGlobal.pojoSe.SeVcRecharge; |
| | | import com.dy.pipIrrGlobal.pojoSe.SeVirtualCard; |
| | | import com.dy.pipIrrSell.result.SellResultCode; |
| | | import com.dy.pipIrrSell.virtualCard.dto.DtoVirtualCard; |
| | | import com.dy.pipIrrSell.virtualCard.enums.LastOperateENUM; |
| | | import com.dy.pipIrrSell.virtualCard.enums.OrderStateENUM; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * @author ZhuBaoMin |
| | | * @date 2024-03-06 8:41 |
| | | * @LastEditTime 2024-03-06 8:41 |
| | | * @Description |
| | | */ |
| | | |
| | | @Slf4j |
| | | @Service |
| | | public class VirtualCardSv { |
| | | @Autowired |
| | | private SeVirtualCardMapper seVirtualCardMapper; |
| | | |
| | | @Autowired |
| | | private SeVcRechargeMapper seVcRechargeMapper; |
| | | |
| | | /** |
| | | * 注册虚拟卡 |
| | | * @param po |
| | | * @return |
| | | */ |
| | | public Long insertVirtualCard(SeVirtualCard po) { |
| | | seVirtualCardMapper.insert(po); |
| | | return po.getId(); |
| | | } |
| | | |
| | | /** |
| | | * 修改虚拟卡 |
| | | * 充值、消费、申请退款、审核退款时需要修改虚拟卡的:余额、最后操作、最后操作时间 |
| | | * @param po |
| | | * @return |
| | | */ |
| | | public Integer updateVirtualCard(SeVirtualCard po) { |
| | | return seVirtualCardMapper.updateByPrimaryKeySelective(po); |
| | | } |
| | | |
| | | /** |
| | | * 根据虚拟卡编号获取虚拟卡对象 |
| | | * @param virtualId |
| | | * @return |
| | | */ |
| | | public SeVirtualCard selectVirtuCardById(Long virtualId) { |
| | | return seVirtualCardMapper.selectByPrimaryKey(virtualId); |
| | | } |
| | | /** |
| | | * 添加虚拟卡充值记录 |
| | | * JSAPI下单后生成部分充值记录 |
| | | * @param po |
| | | * @return |
| | | */ |
| | | public BaseResponse<Boolean> insertVCRecharge(DtoVirtualCard po) { |
| | | String orderNumber = po.getOrderNumber(); |
| | | Long virtualId = po.getVirtualId(); |
| | | Long clientId = po.getClientId(); |
| | | Integer rechargeAmount = po.getRechargeAmount(); |
| | | |
| | | // 验证该虚拟卡账户是否存在并取出当前账户余额 |
| | | SeVirtualCard seVirtualCard = seVirtualCardMapper.selectByPrimaryKey(virtualId); |
| | | if(seVirtualCard == null) { |
| | | return BaseResponseUtils.buildFail(SellResultCode.NO_ACCOUNT.getMessage()); |
| | | } |
| | | Double money = seVirtualCard.getMoney(); |
| | | |
| | | // 添加充值记录 |
| | | SeVcRecharge seVcRecharge = new SeVcRecharge(); |
| | | seVcRecharge.setVcId(virtualId); |
| | | seVcRecharge.setClientId(clientId); |
| | | seVcRecharge.setMoney(money); |
| | | seVcRecharge.setOrderNumber(orderNumber); |
| | | seVcRecharge.setRechargeAmount(rechargeAmount); |
| | | seVcRecharge.setOrderTime(new Date()); |
| | | seVcRecharge.setOrderState(OrderStateENUM.NON_PAYMENT.getCode()); |
| | | Integer rec = seVcRechargeMapper.insert(seVcRecharge); |
| | | if(rec == null) { |
| | | return BaseResponseUtils.buildFail(SellResultCode.RECHARGE_FAIL.getMessage()); |
| | | } |
| | | return BaseResponseUtils.buildSuccess(true) ; |
| | | } |
| | | |
| | | /** |
| | | * 修改虚拟卡充值记录 |
| | | * 微信支付通知后: |
| | | * 1. 更新充值表:充值后余额、支付完成时间、订单状态 |
| | | * 2. 更新虚拟卡表:账户余额、最后操作、最后操作时间 |
| | | * @param orderNumber 订单编号 |
| | | * @return |
| | | */ |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public BaseResponse<Boolean> updateVCRecharge(String orderNumber, Date rechargeTime) { |
| | | SeVcRecharge seVcRecharge = seVcRechargeMapper.getVCRechargeByorderNumber(orderNumber); |
| | | if(seVcRecharge == null) { |
| | | return BaseResponseUtils.buildFail(SellResultCode.RECHARGE_NOT_EXIST.getMessage()); |
| | | } |
| | | |
| | | Long virtualId = seVcRecharge.getVcId(); |
| | | Double money = seVcRecharge.getMoney(); |
| | | Integer rechargeAmount = seVcRecharge.getRechargeAmount(); |
| | | Double afterRrecharge = money + rechargeAmount; |
| | | |
| | | seVcRecharge.setAfterRecharge(afterRrecharge); |
| | | seVcRecharge.setRechargeTime(rechargeTime); |
| | | seVcRecharge.setOrderState(OrderStateENUM.PAID.getCode()); |
| | | Integer rec = seVcRechargeMapper.updateByPrimaryKeySelective(seVcRecharge); |
| | | if(rec == null) { |
| | | return BaseResponseUtils.buildFail(SellResultCode.RECHARGE_FAIL.getMessage()); |
| | | } |
| | | |
| | | SeVirtualCard seVirtualCard = seVirtualCardMapper.selectByPrimaryKey(virtualId); |
| | | if(seVirtualCard == null) { |
| | | return BaseResponseUtils.buildFail(SellResultCode.VIRTUAL_CARD_NOT_EXIST.getMessage()); |
| | | } |
| | | seVirtualCard.setMoney(afterRrecharge); |
| | | seVirtualCard.setLastOperate(LastOperateENUM.RECHARGE.getCode()); |
| | | seVirtualCard.setLastOperateTime(new Date()); |
| | | Integer rec2 = seVirtualCardMapper.updateByPrimaryKeySelective(seVirtualCard); |
| | | if(rec2 == null) { |
| | | return BaseResponseUtils.buildFail(SellResultCode.RECHARGE_FAIL.getMessage()); |
| | | } |
| | | return BaseResponseUtils.buildSuccess(true) ; |
| | | } |
| | | |
| | | /** |
| | | * 修改虚拟卡充值记录 |
| | | * 微信小程序支付通知后修改:余额、充值后余额、充值完成时间 |
| | | * @param po |
| | | * @return |
| | | */ |
| | | public Integer updateVCRecharge(SeVcRecharge po) { |
| | | return seVcRechargeMapper.updateByPrimaryKeySelective(po); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dy.pipIrrSell.virtualCard.dto; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jakarta.validation.constraints.NotBlank; |
| | | import jakarta.validation.constraints.NotNull; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * @author ZhuBaoMin |
| | | * @date 2024-03-06 9:40 |
| | | * @LastEditTime 2024-03-06 9:40 |
| | | * @Description |
| | | */ |
| | | |
| | | @Data |
| | | @Schema(name = "虚拟卡充值传入对象") |
| | | public class DtoVirtualCard { |
| | | public static final long serialVersionUID = 202403060943001L; |
| | | |
| | | /** |
| | | * 订单号 |
| | | */ |
| | | @Schema(description = "订单号", requiredMode = Schema.RequiredMode.NOT_REQUIRED) |
| | | @NotBlank(message = "订单号不能为空") |
| | | private String orderNumber; |
| | | |
| | | /** |
| | | * 农户ID |
| | | */ |
| | | @Schema(description = "农户ID", requiredMode = Schema.RequiredMode.NOT_REQUIRED) |
| | | @NotNull(message = "农户ID不能为空") |
| | | private Long clientId; |
| | | |
| | | /** |
| | | * 虚拟卡ID |
| | | */ |
| | | @Schema(description = "虚拟卡ID", requiredMode = Schema.RequiredMode.NOT_REQUIRED) |
| | | @NotNull(message = "虚拟卡ID不能为空") |
| | | private Long virtualId; |
| | | |
| | | /** |
| | | * 充值金额 |
| | | */ |
| | | @Schema(description = "充值金额", requiredMode = Schema.RequiredMode.NOT_REQUIRED) |
| | | @NotNull(message = "充值金额不能为空") |
| | | private Integer rechargeAmount; |
| | | } |
| New file |
| | |
| | | package com.dy.pipIrrSell.virtualCard.enums; |
| | | |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Getter; |
| | | |
| | | /** |
| | | * @author ZhuBaoMin |
| | | * @date 2024-03-06 9:21 |
| | | * @LastEditTime 2024-03-06 9:21 |
| | | * @Description |
| | | */ |
| | | |
| | | @Getter |
| | | @AllArgsConstructor |
| | | public enum LastOperateENUM { |
| | | OPEN_ACCOUNT((byte)1, "开户"), |
| | | RECHARGE((byte)2, "充值"), |
| | | CONSUME((byte)3, "消费"), |
| | | APPLY_REFUND((byte)4, "申请退款"), |
| | | AUDIT_REFUND((byte)5, "退款审核"); |
| | | |
| | | private final Byte code; |
| | | private final String message; |
| | | } |
| New file |
| | |
| | | package com.dy.pipIrrSell.virtualCard.enums; |
| | | |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Getter; |
| | | |
| | | /** |
| | | * @author ZhuBaoMin |
| | | * @date 2024-03-06 10:00 |
| | | * @LastEditTime 2024-03-06 10:00 |
| | | * @Description |
| | | */ |
| | | |
| | | @Getter |
| | | @AllArgsConstructor |
| | | public enum OrderStateENUM { |
| | | NON_PAYMENT((byte)1, "未支付"), |
| | | PAID((byte)2, "已支付"); |
| | | |
| | | private final Byte code; |
| | | private final String message; |
| | | } |
| | |
| | | } |
| | | |
| | | if(walletSv.getWalletByClientId(clientId) != null) { |
| | | return BaseResponseUtils.buildFail(SellResultCode.WALLET_ACCOUNT_EXIST.getMessage()); |
| | | //return BaseResponseUtils.buildFail(SellResultCode.WALLET_ACCOUNT_EXIST.getMessage()); |
| | | } |
| | | |
| | | SeWallet seWallet = new SeWallet(); |
| File was renamed from pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/payment/PayInfo.java |
| | |
| | | package com.dy.pipirrWebChat.payment; |
| | | package com.dy.pipIrrSell.wechatpay; |
| | | |
| | | /** |
| | | * @author ZhuBaoMin |
| | | * @date 2024-02-22 20:20 |
| | | * @LastEditTime 2024-02-22 20:20 |
| | | * @date 2024-03-06 13:49 |
| | | * @LastEditTime 2024-03-06 13:49 |
| | | * @Description |
| | | */ |
| | | public class PayInfo { |
| | | /* |
| | | /** |
| | | * 小程序登录API |
| | | */ |
| | | public static String loginUrl = "https://api.weixin.qq.com/sns/jscode2session"; |
| | | |
| | | /* |
| | | /** |
| | | * 检验登录态 |
| | | */ |
| | | public static String checkSessionUrl = "https://api.weixin.qq.com/wxa/checksession"; |
| | | |
| | | /** |
| | | * 重置登录态 |
| | | */ |
| | | public static String resetUserSessionKeyUrl = "https://api.weixin.qq.com/wxa/resetusersessionkey"; |
| | | |
| | | /** |
| | | * 获取接口调用凭据 |
| | | */ |
| | | public static String tokenUrl = "https://api.weixin.qq.com/cgi-bin/token"; |
| | | |
| | | /** |
| | | * 统一下单API |
| | | */ |
| | | //public static String orderUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder"; |
| | |
| | | /* |
| | | * 支付结果通知API |
| | | */ |
| | | public static String notifyUrl = "https://www.muxiaobao.com/api/Payment/OrderNotify"; |
| | | //public static String notifyUrl = "https://www.muxiaobao.com/api/Payment/OrderNotify"; |
| | | public static String notifyUrl = "https://44978f7456.imdo.co/webchat/payment/orderNotify"; |
| | | |
| | | /* |
| | | * 查询订单API |
| | | */ |
| | | public static String queryUrl = "https://api.mch.weixin.qq.com/pay/orderquery"; |
| | | |
| | | /* |
| | | /** |
| | | * 申请退款API |
| | | */ |
| | | public static String refundUrl = "https://api.mch.weixin.qq.com/secapi/pay/refund"; |
| | | public static String refundUrl = "https://api.mch.weixin.qq.co/v3/refund/domestic/refunds"; |
| | | |
| | | /* |
| | | * 退款通知API |
| | |
| | | /** |
| | | * 私钥文件路径 |
| | | */ |
| | | public static String certFileName = "C:\\webchat\\apiclient_key.pem"; |
| | | public static String privateCertFileName = "C:\\webchat\\apiclient_key.pem"; |
| | | |
| | | public static String publicCertFileName = "C:\\webchat\\wxp_cert.pem"; |
| | | |
| | | /* |
| | | * 微信订单号,优先使用 |
| New file |
| | |
| | | package com.dy.pipIrrSell.wechatpay; |
| | | |
| | | import com.alibaba.fastjson2.JSON; |
| | | import com.alibaba.fastjson2.JSONObject; |
| | | import com.dy.common.aop.SsoAop; |
| | | import com.dy.common.webUtil.BaseResponse; |
| | | import com.dy.common.webUtil.BaseResponseUtils; |
| | | import com.dy.common.webUtil.ResultCodeMsg; |
| | | import com.dy.pipIrrGlobal.pojoSe.SeVirtualCard; |
| | | import com.dy.pipIrrGlobal.pojoSe.SeWebchatLogonState; |
| | | import com.dy.pipIrrGlobal.voSe.VoClient; |
| | | import com.dy.pipIrrSell.client.ClientSv; |
| | | import com.dy.pipIrrSell.result.SellResultCode; |
| | | import com.dy.pipIrrSell.util.AesUtil; |
| | | import com.dy.pipIrrSell.util.PayHelper; |
| | | import com.dy.pipIrrSell.util.RestTemplateUtil; |
| | | import com.dy.pipIrrSell.virtualCard.VirtualCardSv; |
| | | import com.dy.pipIrrSell.virtualCard.dto.DtoVirtualCard; |
| | | import com.dy.pipIrrSell.wechatpay.dto.*; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.media.Content; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import io.swagger.v3.oas.annotations.responses.ApiResponse; |
| | | import io.swagger.v3.oas.annotations.responses.ApiResponses; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import jakarta.servlet.http.HttpServletResponse; |
| | | import jakarta.validation.Valid; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.http.HttpHeaders; |
| | | import org.springframework.http.MediaType; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.validation.BindingResult; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import javax.crypto.NoSuchPaddingException; |
| | | import java.io.IOException; |
| | | import java.security.GeneralSecurityException; |
| | | import java.security.InvalidKeyException; |
| | | import java.security.NoSuchAlgorithmException; |
| | | import java.security.SignatureException; |
| | | import java.security.spec.InvalidKeySpecException; |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.Date; |
| | | import java.util.HashMap; |
| | | import java.util.Map; |
| | | import java.util.Objects; |
| | | |
| | | /** |
| | | * @author ZhuBaoMin |
| | | * @date 2024-03-06 13:49 |
| | | * @LastEditTime 2024-03-06 13:49 |
| | | * @Description |
| | | */ |
| | | |
| | | @Slf4j |
| | | @Tag(name = "微信支付管理", description = "微信支付各种操作") |
| | | @RestController |
| | | @RequestMapping(path="payment") |
| | | @RequiredArgsConstructor |
| | | public class PaymentCtrl { |
| | | private final PaymentSv paymentSv; |
| | | private final RestTemplateUtil restTemplateUtil; |
| | | private final PayHelper payHelper; |
| | | private final VirtualCardSv virtualCardSv; |
| | | private final ClientSv clientSv; |
| | | |
| | | private String privateCertFileName = PayInfo.privateCertFileName; |
| | | private String appid = PayInfo.appid; |
| | | private String mchid = PayInfo.mchid; |
| | | private String schema = PayInfo.schema; |
| | | private String signType = PayInfo.signType; |
| | | private String description = PayInfo.description; |
| | | private String loginUrl = PayInfo.loginUrl; |
| | | private String notifyUrl = PayInfo.notifyUrl; |
| | | private String grantType = PayInfo.grantType; |
| | | private String refundUrl = PayInfo.refundUrl; |
| | | |
| | | // 平台证书公钥 |
| | | private Map CERTIFICATE_MAP = new HashMap(); |
| | | |
| | | /** |
| | | * 登录凭证校验 |
| | | * @param appid 小程序 appId |
| | | * @param secret 小程序 appSecret |
| | | * @param js_code 临时登录凭证code |
| | | * @return |
| | | * @throws Exception |
| | | */ |
| | | @Operation(summary = "登录凭证校验", description = "登录凭证校验") |
| | | @ApiResponses(value = { |
| | | @ApiResponse( |
| | | responseCode = ResultCodeMsg.RsCode.SUCCESS_CODE, |
| | | description = "操作结果:true:成功,false:失败(BaseResponse.content)", |
| | | content = {@Content(mediaType = MediaType.APPLICATION_JSON_VALUE, |
| | | schema = @Schema(implementation = Boolean.class))} |
| | | ) |
| | | }) |
| | | @PostMapping(path = "getSessionId") |
| | | @Transactional(rollbackFor = Exception.class) |
| | | @SsoAop() |
| | | public BaseResponse<Boolean> getSessionId(@RequestParam("appid") String appid, @RequestParam("secret") String secret, @RequestParam("js_code") String js_code) throws Exception { |
| | | Map<String, Object> queryParams = new HashMap<>(); |
| | | queryParams.put("appid", appid); |
| | | queryParams.put("secret", secret); |
| | | queryParams.put("js_code", js_code); |
| | | queryParams.put("grant_type", grantType); |
| | | Map<String, String> headerParams = new HashMap<>(); |
| | | JSONObject job = restTemplateUtil.get(loginUrl, queryParams, headerParams); |
| | | |
| | | if(job.getLong("errcode") != null && job.getLong("errcode") >= -1) { |
| | | return BaseResponseUtils.buildFail("登录凭证校验失败"); |
| | | } |
| | | |
| | | String openid = job.getString("openid"); |
| | | String sessionKey = job.getString("session_key"); |
| | | |
| | | // 检验登录态 |
| | | JSONObject checkSessionKey = payHelper.checkSessionKey(appid, secret, openid, sessionKey); |
| | | if(checkSessionKey != null) { |
| | | Integer errcode = checkSessionKey.getInteger("errcode"); |
| | | String errmsg = checkSessionKey.getString("errmsg"); |
| | | } |
| | | |
| | | // 重置登录态 |
| | | JSONObject resetUserSessionKey = payHelper.resetUserSessionKey(appid, secret, openid, sessionKey); |
| | | if(resetUserSessionKey != null) { |
| | | Integer errcode = checkSessionKey.getInteger("errcode"); |
| | | String errmsg = checkSessionKey.getString("errmsg"); |
| | | String openid_New = checkSessionKey.getString("openid"); |
| | | String sessionKey_New = checkSessionKey.getString("session_key"); |
| | | } |
| | | |
| | | // 添加登录态记录 |
| | | SeWebchatLogonState po = new SeWebchatLogonState(); |
| | | po.setOpenId(openid); |
| | | po.setSessionKey(sessionKey); |
| | | Date createTime = new Date(); |
| | | po.setCreateTime(createTime); |
| | | Long id = paymentSv.insert(po); |
| | | if(id == null || id <= 0) { |
| | | return BaseResponseUtils.buildFail("登录态记录添加失败"); |
| | | } |
| | | String SessionId = String.valueOf(id); |
| | | |
| | | return BaseResponseUtils.buildSuccess(SessionId) ; |
| | | } |
| | | |
| | | /** |
| | | * 下载微信支付平台证书 测试完废除 |
| | | * @return |
| | | * @throws Exception |
| | | */ |
| | | @Operation(summary = "下载平台证书", description = "下载平台证书") |
| | | @ApiResponses(value = { |
| | | @ApiResponse( |
| | | responseCode = ResultCodeMsg.RsCode.SUCCESS_CODE, |
| | | description = "操作结果:true:成功,false:失败(BaseResponse.content)", |
| | | content = {@Content(mediaType = MediaType.APPLICATION_JSON_VALUE, |
| | | schema = @Schema(implementation = Boolean.class))} |
| | | ) |
| | | }) |
| | | @GetMapping(path = "certificates") |
| | | @Transactional(rollbackFor = Exception.class) |
| | | @SsoAop() |
| | | public BaseResponse<Boolean> certificates() throws Exception { |
| | | String method = "GET"; |
| | | String httpUrl = "/v3/certificates"; |
| | | String nonceStr = payHelper.generateRandomString(); |
| | | Long timestamp = System.currentTimeMillis() / 1000; |
| | | |
| | | String header = schema + " " + payHelper.getToken(method, httpUrl, "", nonceStr, timestamp, privateCertFileName); |
| | | |
| | | Map<String, String> headers = new HashMap<>(); |
| | | headers.put("Authorization", header); |
| | | headers.put("Accept", "application/json"); |
| | | JSONObject job_result = restTemplateUtil.getHeaders(PayInfo.certificates,null, headers); |
| | | JSONObject job_headers = job_result.getJSONObject("headers"); |
| | | String wechatpayNonce = job_headers.getJSONArray("Wechatpay-Nonce").getString(0); |
| | | String wechatpaySerial = job_headers.getJSONArray("Wechatpay-Serial").getString(0); |
| | | String wechatpaySignature = job_headers.getJSONArray("Wechatpay-Signature").getString(0); |
| | | String wechatpaySignatureType = job_headers.getJSONArray("Wechatpay-Signature-Type").getString(0); |
| | | String wechatpayTimestamp = job_headers.getJSONArray("Wechatpay-Timestamp").getString(0); |
| | | |
| | | JSONObject job_body = job_result.getJSONObject("body"); |
| | | |
| | | // 构造验签名串 |
| | | String signatureStr = payHelper.responseSign(wechatpayTimestamp, wechatpayNonce, job_body.toJSONString()); |
| | | // 验证签名 |
| | | Boolean valid = payHelper.responseSignVerify(wechatpaySerial, signatureStr, wechatpaySignature); |
| | | |
| | | return BaseResponseUtils.buildSuccess(); |
| | | } |
| | | |
| | | /** |
| | | * JSAPI下单 |
| | | * @param order 下单请求对象,包含需要传入的参数 |
| | | * @param bindingResult |
| | | * @return |
| | | */ |
| | | @Operation(summary = "JSAPI下单", description = "JSAPI下单") |
| | | @ApiResponses(value = { |
| | | @ApiResponse( |
| | | responseCode = ResultCodeMsg.RsCode.SUCCESS_CODE, |
| | | description = "操作结果:true:成功,false:失败(BaseResponse.content)", |
| | | content = {@Content(mediaType = MediaType.APPLICATION_JSON_VALUE, |
| | | schema = @Schema(implementation = Boolean.class))} |
| | | ) |
| | | }) |
| | | @PostMapping(path = "placeOrder") |
| | | @Transactional(rollbackFor = Exception.class) |
| | | @SsoAop() |
| | | public BaseResponse<Boolean> placeOrder(@RequestBody @Valid DtoOrder order, BindingResult bindingResult) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeySpecException, IOException, SignatureException, InvalidKeyException { |
| | | if(bindingResult != null && bindingResult.hasErrors()){ |
| | | return BaseResponseUtils.buildFail(Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage()); |
| | | } |
| | | // 接收参数:登录态ID、农户ID、虚拟卡ID、充值金额 |
| | | String sessionId = order.getSessionId(); |
| | | Long virtualId = order.getVirtualId(); |
| | | Integer rechargeAmount = order.getRechargeAmount(); |
| | | |
| | | String prepayId = ""; |
| | | SeWebchatLogonState po = paymentSv.selectOne(Long.parseLong(sessionId)); |
| | | String openid = po.getOpenId(); |
| | | |
| | | SeVirtualCard seVirtualCard = virtualCardSv.selectVirtuCardById(virtualId); |
| | | Long clientId = seVirtualCard.getClientId(); |
| | | |
| | | VoClient voClient = clientSv.getOneClient(clientId); |
| | | String clientNum = voClient.getClientNum(); |
| | | |
| | | // 生成订单号并添加充值记录 |
| | | SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmssSSS"); |
| | | String orderNumber = clientNum + dateFormat.format(new Date()); |
| | | |
| | | // 生成虚拟卡充值记录(部分字段) |
| | | DtoVirtualCard virtualCard = new DtoVirtualCard(); |
| | | virtualCard.setOrderNumber(orderNumber); |
| | | virtualCard.setClientId(clientId); |
| | | virtualCard.setVirtualId(virtualId); |
| | | virtualCard.setRechargeAmount(rechargeAmount); |
| | | BaseResponse result = virtualCardSv.insertVCRecharge(virtualCard); |
| | | if(!result.getCode().equals("0001")) { |
| | | return BaseResponseUtils.buildFail(SellResultCode.RECHARGE_ADD_FAIL.getMessage()); |
| | | } |
| | | |
| | | JSONObject job_body = new JSONObject(); |
| | | job_body.put("appid", appid); |
| | | job_body.put("mchid", mchid); |
| | | job_body.put("description", description); |
| | | job_body.put("out_trade_no", orderNumber); |
| | | job_body.put("notify_url", notifyUrl); |
| | | |
| | | //订单金额 |
| | | JSONObject job_amount = new JSONObject(); |
| | | job_amount.put("total", 1); |
| | | job_amount.put("currency", "CNY"); |
| | | job_body.put("amount", job_amount); |
| | | |
| | | //支付者 |
| | | JSONObject job_payer = new JSONObject(); |
| | | job_payer.put("openid", openid); |
| | | job_body.put("payer", job_payer); |
| | | |
| | | // 获取随机串和时间戳,放在此处以保证 |
| | | String nonceStr = payHelper.generateRandomString(); |
| | | Long timestamp = System.currentTimeMillis() / 1000; |
| | | |
| | | String method = "POST"; |
| | | String httpUrl = "/v3/pay/transactions/jsapi"; |
| | | |
| | | String body = job_body.toJSONString(); |
| | | String header = schema + " " + payHelper.getToken(method, httpUrl, body, nonceStr, timestamp, privateCertFileName); |
| | | |
| | | Map<String, String> headers = new HashMap<>(); |
| | | headers.put("Authorization", header); |
| | | headers.put("Accept", "application/json"); |
| | | headers.put("Content-Type", "application/json"); |
| | | |
| | | JSONObject job_result = restTemplateUtil.post(PayInfo.orderUrl, body, headers); |
| | | if(job_result != null) { |
| | | System.out.println(job_result.toString()); |
| | | prepayId = job_result.getString("prepay_id"); |
| | | } |
| | | |
| | | return BaseResponseUtils.buildSuccess(prepayId) ; |
| | | } |
| | | |
| | | /** |
| | | * 申请退款 |
| | | * @param po 退款请求对象 |
| | | * @param bindingResult |
| | | * @return |
| | | * @throws NoSuchPaddingException |
| | | * @throws NoSuchAlgorithmException |
| | | * @throws InvalidKeySpecException |
| | | * @throws IOException |
| | | * @throws SignatureException |
| | | * @throws InvalidKeyException |
| | | */ |
| | | @Operation(summary = "申请退款", description = "申请退款") |
| | | @ApiResponses(value = { |
| | | @ApiResponse( |
| | | responseCode = ResultCodeMsg.RsCode.SUCCESS_CODE, |
| | | description = "操作结果:true:成功,false:失败(BaseResponse.content)", |
| | | content = {@Content(mediaType = MediaType.APPLICATION_JSON_VALUE, |
| | | schema = @Schema(implementation = Boolean.class))} |
| | | ) |
| | | }) |
| | | @PostMapping(path = "refunds") |
| | | @Transactional(rollbackFor = Exception.class) |
| | | @SsoAop() |
| | | public BaseResponse<Boolean> refunds(@RequestBody @Valid Refund po, BindingResult bindingResult) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeySpecException, IOException, SignatureException, InvalidKeyException { |
| | | if(bindingResult != null && bindingResult.hasErrors()){ |
| | | return BaseResponseUtils.buildFail(Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage()); |
| | | } |
| | | |
| | | /** |
| | | * 1. 判断交易时间是否超过一年 |
| | | * 2. 判断退款总金额是否超过订单金额 |
| | | * 3. 判断当前订单退款次数是否超过50次 |
| | | * 4. 判断与该订单上次退款是否相隔1分钟 |
| | | */ |
| | | |
| | | String tradeNo = po.getTradeNo(); |
| | | String refundNo = po.getRefundNo(); |
| | | Integer refund = po.getRefund(); |
| | | if(refundNo == null || refundNo.length() <= 0) { |
| | | // 新提退款申请,生成退款单号 |
| | | //refundNo = generateRefundNo(tradeNo); |
| | | } |
| | | |
| | | // 根据订单号获取总支付金额和总退款金额 |
| | | Integer totalTradeAmount = 0; |
| | | Integer totalRefundAmount = 0; |
| | | //Integer totalTradeAmount = getTotalTradeAmount(tradeNo); |
| | | //Integer totalRefundAmount = getTotalRefundAmount(tradeNo); |
| | | if(totalRefundAmount > totalTradeAmount) { |
| | | return BaseResponseUtils.buildFail(SellResultCode.TOTAL_REFUND_EXCEED_TRADE.getMessage()); |
| | | } |
| | | |
| | | // 生成body |
| | | RefundRequest.Amount amount = new RefundRequest.Amount(); |
| | | amount.setRefund(refund); |
| | | amount.setTotal(totalTradeAmount); |
| | | amount.setCurrency("CNY"); |
| | | |
| | | RefundRequest refundRequest = new RefundRequest(); |
| | | refundRequest.setOut_trade_no(tradeNo); |
| | | refundRequest.setOut_refund_no(refundNo); |
| | | refundRequest.setNotify_url(notifyUrl); |
| | | refundRequest.setAmount(amount); |
| | | |
| | | // 生成header |
| | | String nonceStr = payHelper.generateRandomString(); |
| | | Long timestamp = System.currentTimeMillis() / 1000; |
| | | |
| | | String method = "POST"; |
| | | String httpUrl = "/v3/refund/domestic/refunds"; |
| | | |
| | | String body = JSONObject.toJSONString(refundRequest); |
| | | String header = schema + " " + payHelper.getToken(method, httpUrl, body, nonceStr, timestamp, privateCertFileName); |
| | | |
| | | Map<String, String> headers = new HashMap<>(); |
| | | headers.put("Authorization", header); |
| | | headers.put("Accept", "application/json"); |
| | | headers.put("Content-Type", "application/json"); |
| | | |
| | | JSONObject job_refundResponse = restTemplateUtil.post(PayInfo.orderUrl, body, headers); |
| | | RefundResponse refundResponse = JSON.parseObject(job_refundResponse.toJSONString(), RefundResponse.class); |
| | | |
| | | String status = refundResponse.getStatus(); |
| | | if(status != null && status.equals("SUCCESS")) { |
| | | // 退款申请已受理 |
| | | return BaseResponseUtils.buildSuccess(true) ; |
| | | } else if(status != null && status.equals("PROCESSING")) { |
| | | // 退款处理中 |
| | | return BaseResponseUtils.buildFail(SellResultCode.PROCESSING.getMessage()); |
| | | } else { |
| | | // 退款异常 |
| | | return BaseResponseUtils.buildError(SellResultCode.ABNORMAL.getMessage()); |
| | | } |
| | | |
| | | } |
| | | |
| | | /** |
| | | * 支付通知/退款结果通知 |
| | | * @param headers |
| | | * @param orderNotify |
| | | * @param response |
| | | * @return |
| | | * @throws IOException |
| | | * @throws GeneralSecurityException |
| | | */ |
| | | @Operation(summary = "支付通知", description = "支付通知") |
| | | @ApiResponses(value = { |
| | | @ApiResponse( |
| | | responseCode = ResultCodeMsg.RsCode.SUCCESS_CODE, |
| | | description = "操作结果:true:成功,false:失败(BaseResponse.content)", |
| | | content = {@Content(mediaType = MediaType.APPLICATION_JSON_VALUE, |
| | | schema = @Schema(implementation = Boolean.class))} |
| | | ) |
| | | }) |
| | | @PostMapping(path = "orderNotify", consumes = MediaType.APPLICATION_JSON_VALUE) |
| | | @Transactional(rollbackFor = Exception.class) |
| | | @SsoAop() |
| | | public JSONObject orderNotify(@RequestHeader HttpHeaders headers, @RequestBody OrderNotify orderNotify, HttpServletResponse response) throws IOException, GeneralSecurityException { |
| | | JSONObject result = new JSONObject(); |
| | | |
| | | /** |
| | | * 1.验签处理 |
| | | * 从header中取出4个子参数,同时取出body |
| | | * 验时间差,超过5分钟的不处理 |
| | | * 验证签名 |
| | | * 验证书序列号,必须与某一个证书的序列号一致 |
| | | */ |
| | | String wechatpayNonce = String.valueOf(headers.get("Wechatpay-Nonce").get(0)); |
| | | String wechatpaySerial = String.valueOf(headers.get("Wechatpay-Serial").get(0)); |
| | | String wechatpaySignature = String.valueOf(headers.get("Wechatpay-Signature").get(0)); |
| | | String wechatpayTimestamp = String.valueOf(headers.get("Wechatpay-Timestamp").get(0)); |
| | | String bodyStr = JSONObject.toJSONString(orderNotify); |
| | | |
| | | // 验时间戳,时间差大于5分钟的拒绝 |
| | | Long timeDiff = (System.currentTimeMillis() / 1000 - Long.parseLong(wechatpayTimestamp))/60; |
| | | if(timeDiff > 5) { |
| | | response.setStatus(500); |
| | | result.put("code", "FAIL"); |
| | | result.put("message", "失败"); |
| | | return result; |
| | | } |
| | | |
| | | // 构造验签名串 |
| | | String signatureStr = payHelper.responseSign(wechatpayTimestamp, wechatpayNonce, bodyStr); |
| | | // 验证签名 |
| | | Boolean valid = payHelper.responseSignVerify(wechatpaySerial, signatureStr, wechatpaySignature); |
| | | if(!valid) { |
| | | response.setStatus(500); |
| | | result.put("code", "FAIL"); |
| | | result.put("message", "失败"); |
| | | return result; |
| | | } |
| | | |
| | | // 序列号验证要放在验签后,因为验签时可能会下载新的证书 |
| | | boolean SerialIsValid = false; |
| | | for (String key : payHelper.CERTIFICATE_MAP.keySet()) { |
| | | if(key.equals(wechatpaySerial)) { |
| | | SerialIsValid = true; |
| | | } |
| | | } |
| | | if(!SerialIsValid) { |
| | | response.setStatus(500); |
| | | result.put("code", "FAIL"); |
| | | result.put("message", "失败"); |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * 解密处理 |
| | | * 1 |
| | | */ |
| | | String eventType = orderNotify.getEvent_type(); |
| | | |
| | | if(eventType != null && eventType.equals("TRANSACTION.SUCCESS")) { |
| | | // 支付成功回调 |
| | | /** |
| | | * 支付成功的回调 |
| | | * 取出通知数据对象,继而取出解密所需的associatedData和nonce,以及密文ciphertext |
| | | * 解密ciphertext得到 |
| | | */ |
| | | OrderNotify.NotifyResource notifyResource = orderNotify.getResource(); |
| | | String associatedData = notifyResource.getAssociated_data(); |
| | | String nonce = notifyResource.getNonce(); |
| | | String ciphertext = notifyResource.getCiphertext(); |
| | | |
| | | String resource = AesUtil.decryptToString(PayInfo.key.getBytes("utf-8"), associatedData.getBytes("utf-8"), nonce.getBytes("utf-8"), ciphertext); |
| | | JSONObject job_resource = JSONObject.parseObject(resource); |
| | | |
| | | // 解密后取出:商户订单员、微信支付订单号、交易状态、支付完成时间 |
| | | String out_trade_no = job_resource.getString("out_trade_no"); |
| | | String transaction_id = job_resource.getString("transaction_id"); |
| | | String trade_state = job_resource.getString("trade_state"); |
| | | Date success_time = job_resource.getDate("success_time"); |
| | | |
| | | // 更新虚拟卡表及充值表响应字段 |
| | | BaseResponse result_ = virtualCardSv.updateVCRecharge(out_trade_no, success_time); |
| | | if(!result_.getCode().equals("0001")) { |
| | | response.setStatus(500); |
| | | result.put("code", "FAIL"); |
| | | result.put("message", "失败"); |
| | | return result; |
| | | } |
| | | } else if(eventType != null && eventType.equals("REFUND.SUCCESS")) { |
| | | // 退款成功后回调 |
| | | |
| | | } |
| | | |
| | | // 通知应答 |
| | | response.setStatus(200); |
| | | result.put("code", "SUCCESS"); |
| | | result.put("message", "成功"); |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * 再次签名 |
| | | * @param prepayId 预支付交易会话标识 |
| | | * @return 小程序调起支付参数 |
| | | * @throws Exception |
| | | */ |
| | | @Operation(summary = "再次签名", description = "再次签名") |
| | | @ApiResponses(value = { |
| | | @ApiResponse( |
| | | responseCode = ResultCodeMsg.RsCode.SUCCESS_CODE, |
| | | description = "操作结果:true:成功,false:失败(BaseResponse.content)", |
| | | content = {@Content(mediaType = MediaType.APPLICATION_JSON_VALUE, |
| | | schema = @Schema(implementation = Boolean.class))} |
| | | ) |
| | | }) |
| | | @GetMapping(path = "/signAgain") |
| | | @Transactional(rollbackFor = Exception.class) |
| | | @SsoAop() |
| | | public BaseResponse<JSONObject> signAgain(@RequestParam("prepayId") String prepayId) throws Exception { |
| | | |
| | | // 获取随机串和时间戳,放在此处以保证 |
| | | String appid = PayInfo.appid; |
| | | String timestamp = String.valueOf(System.currentTimeMillis() / 1000); |
| | | String nonceStr = payHelper.generateRandomString(); |
| | | String pkg = "prepay_id=" + prepayId; |
| | | String message = payHelper.buildMessage_signAgain(appid, timestamp, nonceStr, pkg); |
| | | String paySign = payHelper.sign(message.getBytes("utf-8"), privateCertFileName); |
| | | |
| | | JSONObject job_result = new JSONObject(); |
| | | job_result.put("timestamp", timestamp); |
| | | job_result.put("nonceStr", nonceStr); |
| | | job_result.put("package", pkg); |
| | | job_result.put("signType", signType); |
| | | job_result.put("paySign", paySign); |
| | | |
| | | return BaseResponseUtils.buildSuccess(job_result) ; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dy.pipIrrSell.wechatpay; |
| | | |
| | | import com.dy.pipIrrGlobal.daoSe.SeVcRechargeMapper; |
| | | import com.dy.pipIrrGlobal.daoSe.SeWebchatLogonStateMapper; |
| | | import com.dy.pipIrrGlobal.pojoSe.SeVcRecharge; |
| | | import com.dy.pipIrrGlobal.pojoSe.SeWebchatLogonState; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | /** |
| | | * @author ZhuBaoMin |
| | | * @date 2024-03-06 13:51 |
| | | * @LastEditTime 2024-03-06 13:51 |
| | | * @Description |
| | | */ |
| | | |
| | | @Slf4j |
| | | @Service |
| | | public class PaymentSv { |
| | | @Autowired |
| | | private SeWebchatLogonStateMapper seWebchatLogonStateMapper; |
| | | |
| | | @Autowired |
| | | private SeVcRechargeMapper seVcRechargeMapper; |
| | | |
| | | /** |
| | | * 添加登录态状态记录 |
| | | * @param po |
| | | * @return |
| | | */ |
| | | Long insert(SeWebchatLogonState po) { |
| | | seWebchatLogonStateMapper.insert(po); |
| | | return po.getId(); |
| | | } |
| | | |
| | | /** |
| | | * 根据登录态ID获取登录态对象 |
| | | * @param id |
| | | * @return |
| | | */ |
| | | SeWebchatLogonState selectOne(Long id) { |
| | | return seWebchatLogonStateMapper.selectByPrimaryKey(id); |
| | | } |
| | | |
| | | /** |
| | | * 添加虚拟卡充值记录 |
| | | * @param po |
| | | * @return |
| | | */ |
| | | Long insertVCRecharge(SeVcRecharge po) { |
| | | seVcRechargeMapper.insert(po); |
| | | return po.getId(); |
| | | } |
| | | } |
| File was renamed from pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/payment/dto/Code2Session.java |
| | |
| | | package com.dy.pipirrWebChat.payment.dto; |
| | | package com.dy.pipIrrSell.wechatpay.dto; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jakarta.validation.constraints.NotBlank; |
| | |
| | | |
| | | /** |
| | | * @author ZhuBaoMin |
| | | * @date 2024-02-22 15:34 |
| | | * @LastEditTime 2024-02-22 15:34 |
| | | * @date 2024-03-06 13:53 |
| | | * @LastEditTime 2024-03-06 13:53 |
| | | * @Description |
| | | */ |
| | | |
| New file |
| | |
| | | package com.dy.pipIrrSell.wechatpay.dto; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jakarta.validation.constraints.NotBlank; |
| | | import jakarta.validation.constraints.NotNull; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * @author ZhuBaoMin |
| | | * @date 2024-03-06 13:54 |
| | | * @LastEditTime 2024-03-06 13:54 |
| | | * @Description 下单请求对象,下单时传给JSAPI下单接口 |
| | | */ |
| | | |
| | | @Data |
| | | @Schema(name = "下单请求对象") |
| | | public class DtoOrder { |
| | | public static final long serialVersionUID = 202403012108001L; |
| | | |
| | | /** |
| | | * 登录态ID,用来获取openID |
| | | */ |
| | | @Schema(description = "登录态ID", requiredMode = Schema.RequiredMode.NOT_REQUIRED) |
| | | @NotBlank(message = "登录态ID不能为空") |
| | | private String sessionId; |
| | | |
| | | /** |
| | | * 虚拟卡编号,外键,用来获取虚拟卡余额 |
| | | */ |
| | | @Schema(description = "虚拟卡编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED) |
| | | @NotNull(message = "虚拟卡编号不能为空") |
| | | private Long virtualId; |
| | | |
| | | /** |
| | | * 充值金额金额 |
| | | */ |
| | | @Schema(description = "支付金额", requiredMode = Schema.RequiredMode.NOT_REQUIRED) |
| | | @NotNull(message = "虚支付金额不能为空") |
| | | private Integer rechargeAmount; |
| | | } |
| New file |
| | |
| | | package com.dy.pipIrrSell.wechatpay.dto; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * @author ZhuBaoMin |
| | | * @date 2024-03-06 13:54 |
| | | * @LastEditTime 2024-03-06 13:54 |
| | | * @Description 支付和退款通知接收对象 |
| | | */ |
| | | |
| | | @Data |
| | | @Schema(name = "支付和退款回调对象") |
| | | public class OrderNotify { |
| | | public static final long serialVersionUID = 202402291431001L; |
| | | |
| | | /** |
| | | * 通知ID |
| | | */ |
| | | private String id; |
| | | |
| | | /** |
| | | * 通知创建时间 |
| | | */ |
| | | private String create_time; |
| | | |
| | | /** |
| | | * 通知类型 |
| | | * 支付成功通知的类型为:TRANSACTION.SUCCESS |
| | | */ |
| | | private String event_type; |
| | | |
| | | /** |
| | | * 通知数据类型 |
| | | * 支付成功通知为:encrypt-resource |
| | | */ |
| | | private String resource_type; |
| | | |
| | | /** |
| | | * 通知数据 |
| | | */ |
| | | private NotifyResource resource; |
| | | |
| | | /** |
| | | * 回调摘要,退款通知无此属性 |
| | | */ |
| | | private String summary; |
| | | |
| | | |
| | | @Data |
| | | public class NotifyResource { |
| | | |
| | | /** |
| | | * 加密算法类型 |
| | | * 仅支持AEAD_AES_256_GCM |
| | | */ |
| | | private String algorithm; |
| | | |
| | | /** |
| | | * 数据密文 |
| | | */ |
| | | private String ciphertext; |
| | | |
| | | /** |
| | | * 附加数据 |
| | | */ |
| | | public String associated_data; |
| | | |
| | | /** |
| | | * 原始类型 |
| | | * 原始回调类型为:transaction |
| | | */ |
| | | private String original_type; |
| | | |
| | | /** |
| | | * 随机串 |
| | | */ |
| | | private String nonce; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dy.pipIrrSell.wechatpay.dto; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jakarta.validation.constraints.NotBlank; |
| | | import jakarta.validation.constraints.NotNull; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * @author ZhuBaoMin |
| | | * @date 2024-03-06 13:55 |
| | | * @LastEditTime 2024-03-06 13:55 |
| | | * @Description 退款请求对象,小程序调用接口时传参所用对象 |
| | | */ |
| | | |
| | | @Data |
| | | @Schema(name = "退款请求对象") |
| | | public class Refund { |
| | | public static final long serialVersionUID = 202403011607001L; |
| | | |
| | | /** |
| | | * 商户订单号 |
| | | */ |
| | | @Schema(description = "商户订单号", requiredMode = Schema.RequiredMode.NOT_REQUIRED) |
| | | @NotBlank(message = "商户订单号不能为空") |
| | | private String tradeNo; |
| | | |
| | | /** |
| | | * 退款单号,退款单号无值:新提交退款申请。退款单号有值:重新提交退款 |
| | | */ |
| | | @Schema(description = "退款单号", requiredMode = Schema.RequiredMode.NOT_REQUIRED) |
| | | private String refundNo; |
| | | |
| | | /** |
| | | * 退款金额 |
| | | */ |
| | | @Schema(description = "退款金额", requiredMode = Schema.RequiredMode.NOT_REQUIRED) |
| | | @NotNull(message = "退款金额不能为空") |
| | | private Integer refund; |
| | | } |
| New file |
| | |
| | | package com.dy.pipIrrSell.wechatpay.dto; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jakarta.validation.constraints.NotBlank; |
| | | import jakarta.validation.constraints.NotNull; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * @author ZhuBaoMin |
| | | * @date 2024-03-06 13:55 |
| | | * @LastEditTime 2024-03-06 13:55 |
| | | * @Description 退款请求对象,对象赋值完整后向微信请求退款 |
| | | */ |
| | | |
| | | @Data |
| | | @Schema(name = "退款请求对象") |
| | | public class RefundRequest { |
| | | public static final long serialVersionUID = 202403011540001L; |
| | | |
| | | /** |
| | | * 商户订单号,下单时的订单号 |
| | | */ |
| | | @Schema(description = "商户订单号", requiredMode = Schema.RequiredMode.NOT_REQUIRED) |
| | | @NotBlank(message = "商户订单号不能为空") |
| | | private String out_trade_no; |
| | | |
| | | /** |
| | | * 商户退款单号,订单号前加前缀“R” |
| | | */ |
| | | @Schema(description = "商户退款单号", requiredMode = Schema.RequiredMode.NOT_REQUIRED) |
| | | @NotBlank(message = "商户退款单号不能为空") |
| | | private String out_refund_no; |
| | | |
| | | /** |
| | | * 退款结果回调url,refundUrl |
| | | */ |
| | | @Schema(description = "退款结果回调url", requiredMode = Schema.RequiredMode.NOT_REQUIRED) |
| | | @NotBlank(message = "退款结果回调url不能为空") |
| | | private String notify_url; |
| | | |
| | | /** |
| | | * 金额信息 |
| | | */ |
| | | @Schema(description = "金额信息", requiredMode = Schema.RequiredMode.NOT_REQUIRED) |
| | | private Amount amount; |
| | | |
| | | @Data |
| | | public static class Amount { |
| | | /** |
| | | * 退款金额 |
| | | */ |
| | | @Schema(description = "退款金额", requiredMode = Schema.RequiredMode.NOT_REQUIRED) |
| | | @NotNull(message = "退款金额不能为空") |
| | | private Integer refund; |
| | | |
| | | /** |
| | | * 原订单金额,根据订单号查询 |
| | | */ |
| | | @Schema(description = "原订单金额", requiredMode = Schema.RequiredMode.NOT_REQUIRED) |
| | | @NotNull(message = "原订单金额不能为空") |
| | | private Integer total; |
| | | |
| | | /** |
| | | * 退款币种,固定为“CNY” |
| | | */ |
| | | @Schema(description = "退款币种", requiredMode = Schema.RequiredMode.NOT_REQUIRED) |
| | | @NotBlank(message = "退款币种不能为空") |
| | | private String currency; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dy.pipIrrSell.wechatpay.dto; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * @author ZhuBaoMin |
| | | * @date 2024-03-06 13:56 |
| | | * @LastEditTime 2024-03-06 13:56 |
| | | * @Description 退款申请返回的对象 |
| | | */ |
| | | |
| | | @Data |
| | | @Schema(name = "退款申请返回对象") |
| | | public class RefundResponse { |
| | | public static final long serialVersionUID = 202403011431001L; |
| | | |
| | | /** |
| | | * 微信支付退款号 |
| | | */ |
| | | private String refund_id; |
| | | |
| | | /** |
| | | * 商户退款单号 |
| | | */ |
| | | private String out_refund_no; |
| | | |
| | | /** |
| | | * 微信支付订单号 |
| | | */ |
| | | private String transaction_id; |
| | | |
| | | /** |
| | | * 商户订单号 |
| | | */ |
| | | private String out_trade_no; |
| | | |
| | | /** |
| | | * 退款渠道 |
| | | */ |
| | | private String channel; |
| | | |
| | | /** |
| | | * 退款入账账户 |
| | | */ |
| | | private String user_received_account; |
| | | |
| | | /** |
| | | * 退款成功时间 |
| | | */ |
| | | private String success_time; |
| | | |
| | | /** |
| | | * 退款创建时间 |
| | | */ |
| | | private String create_time; |
| | | |
| | | /** |
| | | * 退款状态 |
| | | */ |
| | | private String status; |
| | | |
| | | /** |
| | | * 金额信息 |
| | | */ |
| | | private Amount amount; |
| | | |
| | | |
| | | @Data |
| | | private static class Amount { |
| | | |
| | | /** |
| | | * 订单总金额 |
| | | */ |
| | | private Integer total; |
| | | |
| | | /** |
| | | * 退款金额 |
| | | */ |
| | | private Integer refund; |
| | | |
| | | /** |
| | | * 用户支付金额 |
| | | */ |
| | | private Integer payer_total; |
| | | |
| | | /** |
| | | * 用户退款金额 |
| | | */ |
| | | private Integer payer_refund; |
| | | |
| | | /** |
| | | * 应结退款金额 |
| | | */ |
| | | private Integer settlement_refund; |
| | | |
| | | /** |
| | | * 应结订单金额 |
| | | */ |
| | | private Integer settlement_total; |
| | | |
| | | /** |
| | | * 优惠退款金额 |
| | | */ |
| | | private Integer discount_refund; |
| | | |
| | | /** |
| | | * 退款币种 |
| | | */ |
| | | private String currency; |
| | | |
| | | } |
| | | |
| | | } |
| | |
| | | <module>pipIrr-web-gis</module> |
| | | <module>pipIrr-web-sell</module> |
| | | <module>pipIrr-web-project</module> |
| | | <module>pipIrr-web-webchat</module> |
| | | </modules> |
| | | |
| | | <dependencies> |