--微信支付

This commit is contained in:
yc13649764453
2023-05-24 18:13:36 +08:00
parent 4e7aec5b60
commit 068192327c
25 changed files with 3241 additions and 657 deletions

View File

@@ -0,0 +1,104 @@
package com.peanut.common.utils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import java.util.Objects;
/**
* 继承QueryWrapper重写其构造条件方法使条件为null的查询条件不再执行。
* @since Mybatis+ version: 3.x.x
* @author firewolf
*/
public class ExcludeEmptyQueryWrapper<T> extends QueryWrapper<T> {
@Override
public QueryWrapper<T> eq(boolean condition, String column, Object val) {
if("".equals(val) || Objects.isNull(val)){
condition = false;
}else{
condition = true;
}
return super.eq(condition, column, val);
}
@Override
public QueryWrapper<T> ne(boolean condition, String column, Object val) {
condition = !Objects.isNull(val);
return super.ne(condition, column, val);
}
@Override
public QueryWrapper<T> gt(boolean condition, String column, Object val) {
condition = !Objects.isNull(val);
return super.gt(condition, column, val);
}
@Override
public QueryWrapper<T> ge(boolean condition, String column, Object val) {
condition = !Objects.isNull(val);
return super.ge(condition, column, val);
}
@Override
public QueryWrapper<T> lt(boolean condition, String column, Object val) {
condition = !Objects.isNull(val);
return super.lt(condition, column, val);
}
@Override
public QueryWrapper<T> le(boolean condition, String column, Object val) {
condition = !Objects.isNull(val);
return super.le(condition, column, val);
}
@Override
public QueryWrapper<T> like(boolean condition, String column, Object val) {
if("".equals(val) || Objects.isNull(val)){
condition = false;
}else{
condition = true;
}
return super.like(condition, column, val);
}
@Override
public QueryWrapper<T> notLike(boolean condition, String column, Object val) {
condition = !Objects.isNull(val);
return super.notLike(condition, column, val);
}
@Override
public QueryWrapper<T> likeLeft(boolean condition, String column, Object val) {
condition = !Objects.isNull(val);
return super.likeLeft(condition, column, val);
}
@Override
public QueryWrapper<T> likeRight(boolean condition, String column, Object val) {
condition = !Objects.isNull(val);
return super.likeRight(condition, column, val);
}
@Override
public QueryWrapper<T> between(boolean condition, String column, Object val1, Object val2) {
if(Objects.isNull(val1) && Objects.isNull(val2)){
condition = false;
}else if(Objects.isNull(val1)){
return super.lt(true,column,val2);
}else if(Objects.isNull(val2)){
return super.ge(true,column,val1);
}
return super.between(condition, column, val1, val2);
}
@Override
public QueryWrapper<T> notBetween(boolean condition, String column, Object val1, Object val2) {
if(Objects.isNull(val1) && Objects.isNull(val2)){
condition = false;
}else if(Objects.isNull(val1)){
return super.lt(true,column,val2);
}else if(Objects.isNull(val2)){
return super.ge(true,column,val1);
}
return super.notBetween(condition, column, val1, val2);
}
}

View File

@@ -0,0 +1,19 @@
package com.peanut.modules.book.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.peanut.modules.book.entity.PayWechatOrderEntity;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
/**
* 微信订单表
*
* @author yl
* @email yl328572838@163.com
* @date 2022-08-29 15:27:44
*/
@Repository
@Mapper
public interface PayWechatOrderDao extends BaseMapper<PayWechatOrderEntity> {
}

View File

@@ -0,0 +1,75 @@
package com.peanut.modules.book.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
* 微信订单表
*
* @author yl
* @email yl328572838@163.com
* @date 2022-08-29 15:27:44
*/
@Data
@TableName("pay_wechat_order")
public class PayWechatOrderEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
*
*/
@TableId
private Long id;
/**
* 预支付交易会话标识
*/
private String prepayId;
/**
* 用户Id
*/
private Integer customerId;
/**
* 订单编号
*/
private String orderSn;
/**
* 支付金额
*/
private BigDecimal totalAmount;
/**
* 创建订单时间
*/
private Date createTime;
/**
* 系统日志
*/
private String systemLog;
/**
* 交易类型
*/
private String payType;
/**
* 订单Id
*/
private String orderId;
/**
* 购买配置id
*/
private Integer buyOrderId;
}

View File

@@ -0,0 +1,20 @@
package com.peanut.modules.book.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.peanut.common.utils.PageUtils;
import com.peanut.modules.book.entity.PayWechatOrderEntity;
import java.util.Map;
/**
* 微信订单表
*
* @author yl
* @email yl328572838@163.com
* @date 2022-08-29 15:27:44
*/
public interface PayWechatOrderService extends IService<PayWechatOrderEntity> {
PageUtils queryPage(Map<String, Object> params);
}

View File

@@ -0,0 +1,30 @@
package com.peanut.modules.book.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.peanut.common.utils.PageUtils;
import com.peanut.common.utils.Query;
import com.peanut.modules.book.dao.PayWechatOrderDao;
import com.peanut.modules.book.entity.PayWechatOrderEntity;
import com.peanut.modules.book.service.PayWechatOrderService;
import com.peanut.modules.book.service.PayZfbOrderService;
import org.springframework.stereotype.Service;
import java.util.Map;
@Service("payWechatOrderService")
public class PayWechatOrderServiceImpl extends ServiceImpl<PayWechatOrderDao, PayWechatOrderEntity> implements PayWechatOrderService {
@Override
public PageUtils queryPage(Map<String, Object> params) {
IPage<PayWechatOrderEntity> page = this.page(
new Query<PayWechatOrderEntity>().getPage(params),
new QueryWrapper<PayWechatOrderEntity>()
);
return new PageUtils(page);
}
}

View File

@@ -0,0 +1,278 @@
package com.peanut.modules.pay.IOSPay.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.peanut.common.utils.*;
import com.peanut.modules.book.entity.*;
import com.peanut.modules.book.service.*;
import com.peanut.modules.pay.IOSPay.Result;
import com.peanut.modules.pay.IOSPay.constant.VerifyReceiptConstant;
import com.peanut.modules.pay.IOSPay.model.dto.IapRequestDTO;
import com.peanut.modules.pay.IOSPay.model.dto.IapResponseDTO;
import com.peanut.modules.pay.IOSPay.model.entities.IosPayOrderEntity;
import com.peanut.modules.pay.IOSPay.service.IapVerifyReceiptService;
import com.peanut.modules.pay.IOSPay.service.OrderService;
import com.peanut.modules.pay.IOSPay.vo.FailureVo;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@RestController
@RequestMapping(value = "/Ipa")
@RequiredArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
@Configuration
public class AppController {
@Autowired
IapVerifyReceiptService iapVerifyReceiptService;
@Autowired
OrderService orderService;
@Autowired
private MyUserService userService;
@Autowired
private BookBuyConfigService bookBuyConfigService;
@Autowired
private TransactionDetailsService transactionDetailsService;
@Autowired
private PayPaymentOrderService payPaymentOrderService;
@Autowired
private BuyOrderService buyOrderService;
@RequestMapping(value = "/text", method = RequestMethod.POST)
public String verif() {
return "test";
}
/**
* 苹果内购二次校验
*
* @param dto 入参: 订单id, 苹果凭证
* @return 验证结果
*/
@RequestMapping(value = "/veri", method = RequestMethod.POST)
public Result verifyIap(@RequestBody IapRequestDTO dto) {
Lock lock = new ReentrantLock();
lock.lock();
try {
System.out.println("==============veri=================================" + dto);
// 1. 校验入参
if (dto == null)
return Result.error("入参不能为空");
if (dto.getOrderId().isEmpty()) {
return Result.error("订单id不能为空");
}
if (dto.getProductId().isEmpty()) {
return Result.error("订单号不能为空");
}
if (dto.getTransactionId().isEmpty())
return Result.error("内购订单号不能为空");
if (dto.getReceiptData().isEmpty())
return Result.error("凭证不能为空");
IapResponseDTO receipt = iapVerifyReceiptService.verifyIapReceipt(dto.getReceiptData(), dto.isSandBox());
BuyOrderEntity order2 = this.buyOrderService.getOne(new QueryWrapper<BuyOrderEntity>().eq("order_sn", dto.getOrderId()).eq("del_flag", "0"));
order2.setPaymentDate(new Date());
System.out.println("============order2=================" + order2);
this.buyOrderService.updateById(order2);
IosPayOrderEntity order = new IosPayOrderEntity();
//todo 判断状态 订单状态 0-未付款 1-待发货 2-已发货 3-交易成功 4-交易失败
System.out.println("============保存中=================" + order);
String order01 = order.getOrderid();
String order02 = order2.getOrderSn();
if (order01 == order02) {
return Result.ok();
}
else
if (order01 == null ) {
// 3.1 校验bundle id
if (!receipt.getReceipt().getBundle_id().equals(VerifyReceiptConstant.APP_BUNDLE_IDENTIFIER)) {
return Result.error1();
}
if (receipt.getStatus().equals("0")) {
for (IapResponseDTO.AppleOrder appleOrder : receipt.getReceipt().getIn_app()) {
if (appleOrder.getTransaction_id().equals(dto.getTransactionId()) &&
appleOrder.getProduct_id().equals(dto.getProductId())) {
System.out.println("开始进入判断3"+appleOrder.getTransaction_id()+"====="+dto.getTransactionId()+"====="+
appleOrder.getProduct_id()+"====="+dto.getProductId());
order.setOrderid(dto.getOrderId());
order.setReceiptData(dto.getReceiptData());
order.setProductID(dto.getProductId());
order.setTransactionId(dto.getTransactionId());
order.setCustomerOid(dto.getCustomerOid());
order.setOrderStatus(order2.getOrderStatus());
order.setDelFlag(order2.getDelFlag());
order.setCloseOrder(0);
order.setCreateTime(new Date());
order.setFailureflag(dto.getFailureflag());
orderService.saveOrUpdate(order);
String customerid = dto.getCustomerOid();
String subject = "point";
String body = dto.getProductId();
if ("point".equals(subject)) {
// 插入花生币 变动记录
System.out.printf("body====:" + body);
BookBuyConfigEntity bookBuyConfigEntity = this.bookBuyConfigService.getById(Integer.valueOf(body));
String realMoney = bookBuyConfigEntity.getMoney();
Integer money = Integer.valueOf(realMoney);
userService.rechargeHSPoint(Integer.valueOf(customerid), money);
TransactionDetailsEntity transactionDetailsEntity = new TransactionDetailsEntity();
transactionDetailsEntity.setUserId(Integer.valueOf(customerid));
transactionDetailsEntity.setChangeAmount(new BigDecimal(money));
transactionDetailsEntity.setOrderType("充值");
transactionDetailsEntity.setRelationId(order.getId());
transactionDetailsEntity.setRemark("充值");
System.out.println("===============进入了point获取ID=========================" + order.getId());
MyUserEntity user = userService.getById(Integer.valueOf(customerid));
BigDecimal peanutCoin = user.getPeanutCoin();
transactionDetailsEntity.setUserBalance(peanutCoin);
transactionDetailsEntity.setUserName(user.getNickname());
transactionDetailsEntity.setTel(user.getTel());
transactionDetailsService.save(transactionDetailsEntity);
// 插入 花生币 充值记录
PayPaymentOrderEntity payPaymentOrderEntity = new PayPaymentOrderEntity();
payPaymentOrderEntity.setUserId(Integer.valueOf(customerid));
payPaymentOrderEntity.setOrderId(order.getTransactionId());
payPaymentOrderEntity.setRealAmount(new BigDecimal(bookBuyConfigEntity.getRealMoney()));
payPaymentOrderEntity.setRechargeAmount(new BigDecimal(bookBuyConfigEntity.getMoney()));
payPaymentOrderEntity.setRechargeChannel(bookBuyConfigEntity.getQudao());
payPaymentOrderEntity.setRechargeStatus("success");
payPaymentOrderEntity.setSuccessTime(new Date());
payPaymentOrderEntity.setUserName(user.getNickname());
payPaymentOrderEntity.setTel(user.getTel());
payPaymentOrderService.save(payPaymentOrderEntity);
buyOrderService.updateOrderStatus(Integer.valueOf(customerid), dto.getOrderId(), "2");
order.setMoney(Integer.valueOf(bookBuyConfigEntity.getRealMoney()));
order.setUsername(user.getName());
orderService.saveOrUpdate(order);
System.out.println("==========ok================================================================================================================================================");
}
orderService.update();
System.out.println("==========ok================================================================================================================================================");
}
}
return Result.ok0();
}
return Result.ok("第一次付款成功");
}
return Result.nook(500,"订单加载失败");
} finally {
lock.unlock();
}
// return null;
}
/**
* 修改
*/
@RequestMapping(value = "/failure")
// @RequestBody
public Result update(@Validated @RequestBody IapRequestDTO vo) {
System.out.println("=========entity1111================"+vo);
IosPayOrderEntity order = new IosPayOrderEntity();
order.setOrderid(vo.getOrderId());
order.setReceiptData(vo.getReceiptData());
order.setProductID(vo.getProductId());
order.setTransactionId(vo.getTransactionId());
System.out.println("=========dto.getTransactionId()============" + vo.getTransactionId());
order.setCustomerOid(vo.getCustomerOid());
order.setCloseOrder(0);
order.setCreateTime(new Date());
order.setFailureflag(vo.getFailureflag());
System.out.println("============v0===================================================================================="+vo.getFailureflag());
orderService.saveOrUpdate(order);
System.out.println("=======order================"+order);
return Result.updateok();
}
/**
* 查询失败数据接口
*/
// @Transactional
@RequestMapping(value = "/showFailure")
public R showFailure(@RequestParam Map<String, Object> params )throws Exception{
PageUtils pageUtils = orderService.queryPage1(params);
return R.ok().put("page",pageUtils);
}
/**
* 列表
*/
@RequestMapping(value = "/list",method = RequestMethod.GET)
public R list(@RequestParam Map<String, Object> params)throws Exception {
PageUtils pageUtils = orderService.queryPage1(params);
return R.ok().put("page", pageUtils);
}
}

View File

@@ -0,0 +1,150 @@
package com.peanut.modules.pay.applePay.controller;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.peanut.common.utils.R;
import com.peanut.modules.pay.applePay.service.ApplePayService;
import com.peanut.modules.pay.applePay.utils.ApplePayUtil;
import com.peanut.modules.pay.applePay.utils.IPayNotifyPo;
import com.peanut.modules.pay.IOSPay.Result;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@Slf4j
@RestController
@Api(tags = {"苹果内购"})
@RequestMapping(value = "/apple")
public class ApplePayController {
@Autowired
ApplePayService applePayService;
@Autowired
ApplePayUtil applePayUtil;
/**
* @param :TransactionID订单号
* @param :receipt订单加密收据
* @Description: Ios客户端内购支付
*/
// @PostMapping 文档显示都是post请求
@RequestMapping("/applePay")
public R doIosRequest(@RequestParam("receipt") String receipt, @RequestParam("TransactionID") String TransactionID) {
String verifyResult = applePayUtil.buyAppVerify(receipt, 0);
//苹果服务器没有返回验证结果
if (verifyResult == null) {
return R.error(500,"服务器未接收到验证结果!");
// return Result.failed(ResultCode.APPLE_NULL);
} else {
//
return applePayService.getAppPay(verifyResult, TransactionID);
// return applePayService.doIosRequest(TransactionID,receipt,userId);
}
}
/**
* IOS支付成功后通过APP回调服务器
*
* @param iPayNotifyPo
* @return
*/
@ApiOperation("ios支付成功后验证结果")
@RequestMapping(value = "iPayNotify/", method = RequestMethod.POST)
@ResponseBody
public Result<Object> iPayNotify(@RequestBody IPayNotifyPo iPayNotifyPo) {
log.info("ios支付成功后验证结果[前端传递的ios支付参数:{}]", iPayNotifyPo.toString());
System.out.println("支付");
String receipt = iPayNotifyPo.getTransactionReceipt();
// 拿到收据的MD5
String receiptMd5 = SecureUtil.md5(receipt);
// 查询数据库,看是否是己经验证过的该支付收据
// boolean existsIOSReceipt = paymentService.isExistsIOSReceipt(receiptMd5);
// if (existsIOSReceipt) {
// return Result.error("该充值已完成");
// }
// 1.先线上测试 发送平台验证
String verifyResult = ApplePayUtil.buyAppVerify(receipt, 0);
log.info("1苹果返回的参数:{}]", verifyResult);
if (verifyResult == null) {
// 苹果服务器没有返回验证结果
log.info("苹果服务器没有返回验证结果");
return Result.error("订单没有找到");
} else {
// 苹果验证有返回结果
JSONObject job = JSONUtil.parseObj(verifyResult);
log.info("2[苹果验证返回的json串:{}]", job.toString());
String states = job.getStr("status");
if ("21007".equals(states)) {
log.debug("是沙盒环境,应沙盒测试,否则执行下面");
// 是沙盒环境,应沙盒测试,否则执行下面
// 2.再沙盒测试 发送平台验证
verifyResult = ApplePayUtil.buyAppVerify(receipt, 0);
job = JSONUtil.parseObj(verifyResult);
log.debug("3沙盒环境验证返回的json字符串=" + job.toString());
states = job.getStr("status");
}
if ("0".equals(states)) { // 前端所提供的收据是有效的 验证成功
log.debug("前端所提供的收据是有效的 验证成功");
String r_receipt = job.getStr("receipt");
JSONObject returnJson = JSONUtil.parseObj(r_receipt);
String in_app = returnJson.getStr("in_app");
/**
* in_app说明
* 验证票据返回的receipt里面的in_app字段这个字段包含了所有你未完成交易的票据信息。也就是在上面说到的APP完成交易之后这个票据信息就会从in_app中消失。
* 如果APP不完成交易这个票据信息就会在in_app中一直保留。(这个情况可能仅限于你的商品类型为消耗型)
*
* 知道了事件的原委就很好优化解决了方案有2个
* 1.对票据返回的in_app数据全部进行处理没有充值的全部进行充值
* 2.仅对最新的充值信息进行处理(我们采取的方案)
*
* 因为采用一方案:
* 如果用户仅进行了一次充值,该充值未到账,他不再进行充值了,那么会无法导致。
* 如果他通过客服的途径已经进行了补充充值,那么他在下一次充值的时候依旧会把之前的产品票据带回,这时候有可能出现重复充值的情况
*
* 以上说明是我在网上找到的,可以查看原文
* https://www.cnblogs.com/widgetbox/p/8241333.html
*/
JSONArray jsonArray = JSONUtil.parseArray(in_app);
if (jsonArray.size() > 0) {
int index = 0;
JSONObject o = JSONUtil.parseObj(jsonArray.get(index));
String transaction_id = o.getStr("transaction_id"); // 订单号
String product_id = o.getStr("product_id"); // 产品id也就是支付金额
String purchase_date_ms = o.getStr("purchase_date_ms"); // 支付时间
//
// // 添加支付金额
// Result<Object> iosChargeSuccess = paymentService.iosChargeSuccess(transaction_id, product_id, purchase_date_ms, receiptMd5, iPayNotifyPo.getMoneyId(), iPayNotifyPo.getUserId());
// int i =0;
// int a = 0;
// if (iosChargeSuccess.getCode().equals(200)){
// i = paymentService.updateCurrentState(iPayNotifyPo.getPayId(),iPayNotifyPo.getUserId(),a);
// }else {
// a = 1;
// i = paymentService.updateCurrentState(iPayNotifyPo.getPayId(),iPayNotifyPo.getUserId(),a);
// }
//
// return iosChargeSuccess;
}
} else {
return Result.error("收到数据有误");
}
}
return Result.ok();
}
}

View File

@@ -0,0 +1,133 @@
package com.peanut.modules.pay.applePay.controller;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.apache.commons.lang.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import com.alibaba.fastjson.JSONObject;
import org.springframework.web.bind.annotation.RestController;
/**
* @author :
* @date :
* @Version: 1.0
* @Desc : 苹果支付 参考地址https://www.cnblogs.com/shoshana-kong/p/10991753.html
* sendHttpsCoon 方法里面包含了增加的业务逻辑
* 本类引入多种的Json序列化工具选择了Hutool
*/
@RestController
@RequestMapping("iap")
public class IapController {
//购买凭证验证地址
private static final String certificateUrl = "https://buy.itunes.apple.com/verifyReceipt";
//测试的购买凭证验证地址
private static final String certificateUrlTest = "https://sandbox.itunes.apple.com/verifyReceipt";
/**
* 重写X509TrustManager
*/
private static TrustManager myX509TrustManager = new X509TrustManager() {
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
};
/**
* 接收iOS端发过来的购买凭证
* @param userId
* @param receipt
* @param chooseEnv
*/
@RequestMapping("/setIapCertificate")
public String setIapCertificate(String userId, String receiptData, boolean chooseEnv){
if(StringUtils.isEmpty(userId) || StringUtils.isEmpty(receiptData)){
return null;
}
String url = null;
url = chooseEnv == true? certificateUrl:certificateUrlTest;
final String certificateCode = receiptData;
if(StringUtils.isNotEmpty(certificateCode)){
return sendHttpsCoon(url, certificateCode);
}else{
return null;
}
}
/**
* 发送请求
* @param url
* @param strings
* @return
*/
private String sendHttpsCoon(String url, String code){
if(url.isEmpty()){
return null;
}
try {
//设置SSLContext
SSLContext ssl = SSLContext.getInstance("SSL");
ssl.init(null, new TrustManager[]{myX509TrustManager}, null);
//打开连接
HttpsURLConnection conn = (HttpsURLConnection) new URL(url).openConnection();
//设置套接工厂
conn.setSSLSocketFactory(ssl.getSocketFactory());
//加入数据
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setRequestProperty("Content-type","application/json");
JSONObject obj = new JSONObject();
obj.put("receipt-data", code);
BufferedOutputStream buffOutStr = new BufferedOutputStream(conn.getOutputStream());
buffOutStr.write(obj.toString().getBytes());
buffOutStr.flush();
buffOutStr.close();
//获取输入流
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line = null;
StringBuffer sb = new StringBuffer();
while((line = reader.readLine())!= null){
sb.append(line);
}
return sb.toString();
} catch (Exception e) {
return null;
}
}
}

View File

@@ -0,0 +1,36 @@
package com.peanut.modules.pay.applePay.service;
import com.peanut.common.utils.R;
import com.peanut.modules.pay.IOSPay.Result;
import org.springframework.stereotype.Service;
@Service
public interface ApplePayService {
/**
* 看是否是己经验证过的该支付收据
* @param receiptMd5
* @return
*/
public boolean isExistsIOSReceipt(String receiptMd5);
int updateCurrentState(String payId, String userId, int a);
/**
* IOS支付
* @param transaction_id 订单号
* @param product_id 产品id也就是支付金额
* @param purchase_date_ms 支付时间
* @param receiptMd5 拿到收据的MD5
* @param moneyId 金额id
*/
Result<Object> iosChargeSuccess(String transaction_id, String product_id, String purchase_date_ms, String receiptMd5, String moneyId, String userId);
R getAppPay(String verifyResult, String transactionID);
}

View File

@@ -0,0 +1,214 @@
package com.peanut.modules.pay.applePay.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.peanut.common.utils.DateUtils;
import com.peanut.common.utils.R;
import com.peanut.modules.book.entity.*;
import com.peanut.modules.book.service.*;
import com.peanut.modules.pay.IOSPay.Result;
import com.peanut.modules.pay.applePay.service.ApplePayService;
import com.peanut.modules.pay.applePay.utils.*;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
@Service("ApplePayService")
@Slf4j
@AllArgsConstructor
public class ApplePayServiceImpl implements ApplePayService {
// 日志记录系统支付模块记录日志信息输出到sys-pay
private static final Logger logger = LoggerFactory.getLogger("sys-pay");
@Autowired
private MyUserService userService;
@Autowired
private BuyOrderService buyOrderService;
@Autowired
private TransactionDetailsService transactionDetailsService;
@Autowired
private ApplePayUtil applePayUtil;
@Autowired
private BookBuyConfigService bookBuyConfigService;
@Autowired
private PayPaymentOrderService payPaymentOrderService;
@Autowired
public static final String TRADE_TYPE_APP = "APP";
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public boolean isExistsIOSReceipt(String receiptMd5) {
return false;
}
@Resource
private TsBillMapper tsBillMapper;
/**
* 支付成功之后将当前订单的状态改为1
*
* @param payId
* @param userId
* @param a
* @return
*/
@Override
public int updateCurrentState(String payId, String userId, int a) {
return 0;
}
/**
* IOS支付
* @param transaction_id 订单号
* @param product_id 产品id也就是支付金额
* @param purchase_date_ms 支付时间
* @param receiptMd5 拿到收据的MD5
* @param moneyId 金额id
*/
@Override
public Result<Object> iosChargeSuccess(String transaction_id, String product_id, String purchase_date_ms, String receiptMd5, String moneyId, String userId) {
/**
* IOS支付
* @param transaction_id 订单号
* @param product_id 产品id也就是支付金额
* @param purchase_date_ms 支付时间
* @param receiptMd5 拿到收据的MD5
* @param moneyId 金额id
*/
try {
List<Map<String, Object>> queryForList = jdbcTemplate.queryForList("SELECT id,showcoin_num,give_showcoin_num,rmb_price from tsm_payment_config_ios where product_id =? ",
moneyId);
if (queryForList.size() > 0) {
String bill_content = "充值";//账单产生方式
int bill_state = 1;// 充值
int current_state = 1;// 支付成功
int payment_method = 3;// IOS
String date = DateUtils.DATE_TIME_PATTERN;// 当前时间
BigDecimal amountofmoney = new BigDecimal(queryForList.get(0).get("rmb_price").toString());// 金额
String uuid = UUID.randomUUID().toString().replaceAll("-", "");
// 生成消费订单
//String sql = "insert into ts_bill (id,users_id,payment_config_id,bill_num,bill_content,bill_sumofmoney,bill_state,current_state,payment_method,receipt,createtdate) value(?,?,?,?,?,?,?,?,?,?,?);";
//jdbcTemplate.update(sql, uuid,userId,moneyId,transaction_id,bill_content,amountofmoney,bill_state,current_state,payment_method,receiptMd5,date);
String type = "1";// 加
long showcoin_num = Long.parseLong(queryForList.get(0).get("showcoin_num").toString()) + Long.parseLong(queryForList.get(0).get("give_showcoin_num").toString());
// 此操作为需求实际场景可根据用户产品需求更改
//commonServiceI.updShowcoinNum(type, userId, showcoin_num);
return Result.ok("支付成功");
} else {
return Result.error("操作异常,所选金额不存在");
}
} catch (Exception e) {
return Result.error("ios支付异常");
}
}
@Override
public R getAppPay(String verifyResult, String transactionID) {
log.info("##########################苹果支付验证!########################");
JSONObject jsonObject = JSONObject.parseObject(verifyResult);
String status = jsonObject.getString("status");
//判断是否验证成功
if ("0".equals(status)) {
//app端所提供的收据是有效的,验证成功
String receipt = jsonObject.getString("receipt");
JSONObject returnJson = JSONObject.parseObject(receipt);
String in_app = returnJson.getString("in_app");
JSONObject in_appJson = JSONObject.parseObject(in_app.substring(1, in_app.length() - 1));
String transactionId = in_appJson.getString("transaction_id");
String in_app_ownership_type = in_appJson.getString("in_app_ownership_type");
//如果验证后的订单号与app端传来的订单号一致并且状态为已支付状态则处理自己的业务
if (transactionID.equals(transactionId) && "PURCHASED".equals(in_app_ownership_type)) {
//===================处理自己的业务 ============================
BuyOrderEntity order = this.buyOrderService.getOne(new QueryWrapper<BuyOrderEntity>().eq("order_sn", transactionId ));
PayWechatOrderEntity wechat = new PayWechatOrderEntity();
if ("vip".equals(order.getOrderType())) {
//会员记录
BookBuyConfigEntity bookBuyConfigEntity = bookBuyConfigService.getById(wechat.getBuyOrderId());
String month = bookBuyConfigEntity.getMonth();
userService.openMember(Integer.valueOf(transactionId),Integer.valueOf(month));
buyOrderService.updateOrderStatus(Integer.valueOf(transactionId),order.getOrderSn(),"2");
}
if ("order".equals(order.getOrderType())) {
BuyOrderEntity orderEntity = buyOrderService.getBaseMapper().selectOne(new QueryWrapper<BuyOrderEntity>().eq("order_sn", wechat.getOrderId()));
BigDecimal realMoney = orderEntity.getRealMoney();
//更新 订单 记录
}
if ("point".equals(order.getOrderType())) {
BookBuyConfigEntity bookBuyConfigEntity = bookBuyConfigService.getById(wechat.getBuyOrderId());
String realMoney = bookBuyConfigEntity.getRealMoney();
Integer money = Integer.valueOf(realMoney);
userService.rechargeHSPoint(Integer.valueOf(order.getUserId()),money);
TransactionDetailsEntity transactionDetailsEntity = new TransactionDetailsEntity();
transactionDetailsEntity.setUserId(Integer.valueOf(order.getUserId()));
transactionDetailsEntity.setChangeAmount(new BigDecimal(money));
transactionDetailsEntity.setOrderType("充值");
transactionDetailsEntity.setRelationId(wechat.getId().intValue());
transactionDetailsEntity.setRemark("充值");
MyUserEntity user = userService.getById(Integer.valueOf(order.getUserId()));
BigDecimal peanutCoin = user.getPeanutCoin();
transactionDetailsEntity.setUserBalance(peanutCoin);
transactionDetailsEntity.setUserName(user.getNickname());
transactionDetailsEntity.setTel(user.getTel());
transactionDetailsService.save(transactionDetailsEntity);
// 插入 花生币 充值记录
PayPaymentOrderEntity payPaymentOrderEntity = new PayPaymentOrderEntity();
payPaymentOrderEntity.setUserId(Integer.valueOf(order.getUserId()));
payPaymentOrderEntity.setOrderId(String.valueOf(wechat.getId()));
payPaymentOrderEntity.setRealAmount(new BigDecimal(bookBuyConfigEntity.getRealMoney()));
payPaymentOrderEntity.setRechargeAmount(new BigDecimal(bookBuyConfigEntity.getMoney()));
payPaymentOrderEntity.setRechargeChannel(bookBuyConfigEntity.getQudao());
payPaymentOrderEntity.setRechargeStatus("success");
payPaymentOrderEntity.setSuccessTime(new Date());
payPaymentOrderEntity.setUserName(user.getNickname());
payPaymentOrderEntity.setTel(user.getTel());
payPaymentOrderService.save(payPaymentOrderEntity);
buyOrderService.updateOrderStatus(Integer.valueOf(order.getUserId()),order.getOrderSn(),"2");
// 插入花生币 变动记录
}
}
return R.ok();
}
return R.error("苹果内购验证失败");
}}

View File

@@ -0,0 +1,159 @@
package com.peanut.modules.pay.applePay.utils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import sun.misc.BASE64Decoder;
import javax.net.ssl.*;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Locale;
@Component
public class ApplePayUtil {
private static class TrustAnyTrustManager implements X509TrustManager {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[]{};
}
}
private static class TrustAnyHostnameVerifier implements HostnameVerifier {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
}
private static String url_apple_pay = "https://buy.itunes.apple.com/verifyReceipt";
private static String test_url_apple_pay = "https://sandbox.itunes.apple.com/verifyReceipt";
/**
* 苹果服务器验证
*
* @param receipt 账单
* @return null 或返回结果
* @url 要验证的地址 沙盒 https://sandbox.itunes.apple.com/verifyReceipt
*/
public static String buyAppVerify(String receipt, int type) {
//环境判断 线上/开发环境用不同的请求链接
try {
String url = null;
if (type == 0) {
url = test_url_apple_pay; //沙盒环境,测试
} else {
url = url_apple_pay; //线上环境
}
//String url = EnvUtils.isOnline() ?url_verify : url_sandbox;
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, new TrustManager[]{new TrustAnyTrustManager()}, new java.security.SecureRandom());
URL console = new URL(url);
HttpsURLConnection conn = (HttpsURLConnection) console.openConnection();
conn.setSSLSocketFactory(sc.getSocketFactory());
conn.setHostnameVerifier(new TrustAnyHostnameVerifier());
conn.setRequestMethod("POST");
conn.setRequestProperty("content-type", "text/json");
conn.setRequestProperty("Proxy-Connection", "Keep-Alive");
conn.setDoInput(true);
conn.setDoOutput(true);
//r如果有错误就注释
conn.setConnectTimeout(3000);
BufferedOutputStream hurlBufOus = new BufferedOutputStream(conn.getOutputStream());
//拼成固定的格式传给平台
String str = String.format(Locale.CHINA, "{\"receipt-data\":\"" + receipt + "\"}");
// 直接将receipt当参数发到苹果验证就行不用拼格
String str1 = String.format(Locale.CHINA, receipt);
hurlBufOus.write(str.getBytes());
hurlBufOus.flush();
InputStream is = conn.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String line = null;
StringBuilder sb = new StringBuilder();
while ((line = reader.readLine()) != null) {
sb.append(line);
}
return sb.toString();
} catch (Exception ex) {
System.out.println("苹果服务器异常");
ex.printStackTrace();
}
return null;
}
//视情况而定苹果app进行支付然后收到苹果的收据一串很长的BASE64编码的字符串
/**
* 用BASE64加密
*
* @param str
* @return
*/
public static String getBASE64(String str) {
byte[] b = str.getBytes();
String s = null;
if (b != null) {
s = new sun.misc.BASE64Encoder().encode(b);
}
return s;
}
/**
* 解密BASE64字窜
* @param s
* @return
*/
public static String getFromBASE64(String s) {
byte[] b = null;
if (s != null) {
BASE64Decoder decoder = new BASE64Decoder();
try {
b = decoder.decodeBuffer(s);
return new String(b);
} catch (Exception e) {
e.printStackTrace();
}
}
return new String(b);
}
}

View File

@@ -0,0 +1,194 @@
package com.peanut.modules.pay.weChatPay.config;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.*;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.exception.HttpCodeException;
import com.wechat.pay.contrib.apache.httpclient.exception.NotFoundException;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import lombok.Data;
import lombok.Value;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.impl.client.CloseableHttpClient;
import org.junit.After;
import org.junit.Before;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
/**
* @Author:
* @Description:
**/
@Configuration
@Data
@Slf4j
@ConfigurationProperties(prefix = "wxpay") //读取wxpay节点
@PropertySource(value = "classpath:weChatConfig.properties" ) //读取配置文件
//@Component
public class WechatPayConfig implements Serializable {
/**
* APPID
*/
private String appId;
/**
* 商户号
*/
private String mchId;
/**
* 服务商商户号
*/
private String slMchId;
/**
* APIv2密钥
*/
private String apiKey;
/**
* APIv3密钥
*/
private String apiV3Key;
/**
* 支付通知回调地址
*/
private String notifyUrl;
/**
* 退款回调地址
*/
private String refundNotifyUrl;
/**
* API 证书中的 key.pem 商户私钥
*/
private String keyPemPath;
/**
* 商户序列号
*/
private String serialNo;
/**
* 微信支付V3-url前缀
*/
private String baseUrl;
private String domain;
//获取私钥工具
public PrivateKey getPrivateKey(String keyPemPath) {
try {
return PemUtil.loadPrivateKey(new FileInputStream(keyPemPath));
} catch (FileNotFoundException e) {
//抛出异常,并把错误文件继续向上抛出
throw new RuntimeException("私钥文件不存在",e);
}
}
/**
* 获取证书管理器实例 签名验证器
*
* @return
*/
@Bean
public Verifier getVerifier() throws GeneralSecurityException, IOException, HttpCodeException, NotFoundException {
log.info("获取证书管理器实例");
//获取商户私钥
// PrivateKey privateKey = getPrivateKey(keyPemPath);
PrivateKey privateKey = getPrivateKey(keyPemPath);
//私钥签名对象
PrivateKeySigner privateKeySigner = new PrivateKeySigner(serialNo, privateKey);
//身份认证对象
WechatPay2Credentials wechatPay2Credentials = new WechatPay2Credentials(mchId, privateKeySigner);
// 使用定时更新的签名验证器,不需要传入证书
CertificatesManager certificatesManager = CertificatesManager.getInstance();
certificatesManager.putMerchant(
mchId, wechatPay2Credentials, apiV3Key.getBytes(StandardCharsets.UTF_8));
return certificatesManager.getVerifier(mchId);
}
/**
* 获取支付http请求对象
*
* @param verifier
* @return
*/
@Bean(name = "wxPayClient")
// @Bean
public CloseableHttpClient getWxPayClient(Verifier verifier) {
log.info("获取HttpClient");
//获取商户私钥
PrivateKey privateKey = getPrivateKey(keyPemPath);
WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
.withMerchant(mchId, serialNo, privateKey)
.withValidator(new WechatPay2Validator(verifier));
CloseableHttpClient httpClient =builder.build();
// 通过WechatPayHttpClientBuilder构造的HttpClient会自动的处理签名和验签并进行证书自动更新
return httpClient;
}
/**
* 获取HttpClient无需进行应答签名验证跳过验签的流程
*/
@Bean(name = "wxPayNoSignClient")
public CloseableHttpClient getWxPayNoSignClient() {
//获取商户私钥
PrivateKey privateKey = getPrivateKey(keyPemPath);
//用于构造HttpClient
WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
//设置商户信息
.withMerchant(mchId, serialNo, privateKey)
//无需进行签名验证、通过withValidator((response) -> true)实现
.withValidator((response) -> true);
// 通过WechatPayHttpClientBuilder构造的HttpClient会自动的处理签名和验签并进行证书自动更新
return builder.build();
}
}

View File

@@ -0,0 +1,335 @@
package com.peanut.modules.pay.weChatPay.controller;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.peanut.common.utils.R;
import com.peanut.modules.book.entity.*;
import com.peanut.modules.book.service.*;
import com.peanut.modules.pay.weChatPay.dto.WechatDto;
import com.peanut.modules.pay.weChatPay.config.WechatPayConfig;
import com.peanut.modules.pay.weChatPay.util.HttpUtils;
import com.peanut.modules.book.entity.BookBuyConfigEntity;
import com.peanut.modules.pay.weChatPay.util.WechatPayValidator;
import com.peanut.modules.pay.weChatPay.util.WxPayUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.*;
import java.util.concurrent.locks.ReentrantLock;
@Slf4j
@RestController
@CrossOrigin //跨域
@RequestMapping("/pay")
@Configuration
public class WeChatPayController {
@Autowired private MyUserService userService;
@Autowired private BuyOrderService buyOrderService;
@Autowired
@Lazy
private WechatPayConfig wechatPayConfig;
@Autowired private BookBuyConfigService bookBuyConfigService;
@Autowired private PayWechatOrderService payWechatOrderService;
@Autowired private PayPaymentOrderService payPaymentOrderService;
@Autowired private TransactionDetailsService transactionDetailsService;
@Autowired
private CloseableHttpClient wxPayNoSignClient; //无需应答签名
@Autowired
private CloseableHttpClient wxPayClient;
@Autowired
private WxPayUtil wxPayUtil;
private final ReentrantLock lock = new ReentrantLock();
// 由微信生成的应用ID全局唯一。
public static final String appId= "wx47134a8f15083734";
// 直连商户的商户号,由微信支付生成并下发。
public static final String mchId = "1612860909";
// 商户证书序列号 7B5676E3CDF56680D0414A009CE501C844DBE2D6 679AECB2F7AC4183033F713828892BA640E4EEE3
public static final String mchSerialNo = "679AECB2F7AC4183033F713828892BA640E4EEE3";
// 微信下单Url
public static final String payUrl = "https://api.mch.weixin.qq.com/v3/pay/transactions/app";
@RequestMapping(value = "/Vxtext", method = RequestMethod.POST)
public String vxPAYtext(@RequestBody Integer user) {
return "=============================test============"+user;
}
/**
* App 微信下单
*
* @param
* @return
* @PathVariable
*/
@RequestMapping(value = "/placeAnOrder/app")
@Transactional(rollbackFor = Exception.class)
public R pay(@RequestBody WechatDto dto ) throws Exception{
System.out.println("==========ordersn================"+dto.getOrderSn());
log.info("生成订单");
List<BuyOrderEntity> one = this.buyOrderService.getBaseMapper().selectList(new QueryWrapper<BuyOrderEntity>().eq("order_sn", dto.getOrderSn()));
BuyOrderEntity order = one.get(0);
// 获取订单
Map<String,Object> paramMap = new HashMap<>();
paramMap.put("appid",appId);
paramMap.put("mchid",mchId);
paramMap.put("description","微信充值");
// 订单编号
paramMap.put("out_trade_no",order.getOrderSn());
// paramMap.put("attach",""); //自定义数据 支付完成后才能显示 在查询API和支付通知中原样返回可作为自定义参数使用
paramMap.put("notify_url",wechatPayConfig.getNotifyUrl());
// paramMap.put("time_expire","");
// 实收金额0.38乘100=38
BigDecimal realsmoney= order.getRealMoney();
BigDecimal hand=new BigDecimal("100");
realsmoney =realsmoney.multiply(hand) ;
Map<String,Object> amountMap = new HashMap<>();
amountMap.put("total",realsmoney);
amountMap.put("currency","CNY");
paramMap.put("amount",amountMap);
JSONObject json = JSONObject.parseObject(JSON.toJSONString(paramMap));
log.info("请求参数"+paramMap);
com.alibaba.fastjson.JSONObject jsonObject1 = wxPayUtil.doPostWexinV3("https://api.mch.weixin.qq.com/v3/pay/transactions/app", json.toJSONString());
String prepayid = jsonObject1.getString("prepay_id");
// 传入参数 payUrl 发送post请求
HttpPost httpPost = new HttpPost(payUrl);
// 将json数据转换成字符串
StringEntity entity = new StringEntity(json.toString(),"utf-8");
// 设置该请求的Content-Type为application/json 都是json格式
entity.setContentType("application/json");
// 将实体对象设置到HttpPost表示要传递该数据到服务器端。
httpPost.setEntity(entity);
// 设置请求头部的Accept属性为"application/json"表示客户端希望接收的为json。
httpPost.setHeader("Accept", "application/json");
CloseableHttpResponse response = wxPayClient.execute(httpPost);
// 向微信支付平台发送请求,处理响应结果,并将订单信息保存到数据库中。
String bodyAsString = EntityUtils.toString(response.getEntity());//响应体
// 时间戳
Long timestamp = System.currentTimeMillis()/1000;
// 随机串
String nonceStr = UUID.randomUUID().toString().replace("-","");
String sign = wxPayUtil.getSign(WxPayUtil.appId,timestamp,nonceStr,prepayid);
log.info("签名:"+sign);
Map Map = new HashMap();
Map.put("prepayid",prepayid);
Map.put("timestamp",timestamp+"");
Map.put("noncestr",nonceStr);
Map.put("sign",sign);
Map.put("appid",appId);
Map.put("package","Sign=WXPay");
Map.put("extData","sign");
Map.put("partnerid",mchId);
try {
int statusCode = response.getStatusLine().getStatusCode(); //响应状态码
if (statusCode == 200) { //处理成功
System.out.println("成功,返回结果 = " + bodyAsString); //返回响应体 EntityUtils.toString(response.getEntity())
// 添加微信支付订单信息
PayWechatOrderEntity wechat = new PayWechatOrderEntity();
wechat.setCustomerId(order.getUserId()); //用户id
wechat.setCreateTime(new Date()); //创建订单时间
wechat.setOrderSn(order.getOrderSn()); //订单编号
wechat.setPrepayId(prepayid); //预支付回话标识 标识为响应体EntityUtils.toString(response.getEntity())
wechat.setTotalAmount(order.getRealMoney()); //支付实收金额
wechat.setSystemLog(response.toString()); //日志
wechat.setPayType(order.getOrderType()); //交易类型
wechat.setOrderId(order.getOrderSn()); //订单号
wechat.setBuyOrderId(dto.getBuyOrderId()); //购买配置id
this.payWechatOrderService.save(wechat); //微信订单表拿到数据保存数据库
} else if (statusCode == 204) { //处理成功无返回Body
System.out.println("成功");
} else {
System.out.println("下单失败 = " + statusCode + ",返回结果 = " + bodyAsString);
throw new IOException("request failed");
}
}finally {
response.close();
}
// 返回url和订单号
return R.ok().put("paramMap" ,paramMap).put("Map",Map);
}
/**
* 获取私钥。
*
* @param filename 私钥文件路径 (required)
* @return 私钥对象
*/
public static PrivateKey getPrivateKey(String filename) throws Exception {
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 request
* @param response
* @return
*/
@PostMapping("/payNotify")
public R payNotify(HttpServletRequest request, HttpServletResponse response) {
log.info("##############################微信支付回调#######################");
PayWechatOrderEntity wechatEntity = new PayWechatOrderEntity();
// 处理通知参数
Map<String,Object> bodyMap = getNotifyBody(request);
if(bodyMap==null){
return null;
}
log.warn("=========== 在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱 ===========");
if(lock.tryLock()) {
try {
// 解密resource中的通知数据
String resource = bodyMap.get("resource").toString();
Map<String, Object> resourceMap = WechatPayValidator.decryptFromResource(resource, wechatPayConfig.getApiV3Key(),1);
String orderNo = resourceMap.get("out_trade_no").toString();
String transactionId = resourceMap.get("transaction_id").toString();
System.out.println("===================回调解密transactionId================================"+transactionId);
// 根据订单号,做幂等处理,并且在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱
BuyOrderEntity order = this.buyOrderService.getOne(new QueryWrapper<BuyOrderEntity>().eq("order_sn", orderNo));
if(!ObjectUtils.isEmpty(order)){
wechatEntity = this.payWechatOrderService.getOne(new QueryWrapper<PayWechatOrderEntity>().eq("order_id", order.getOrderId()));
}else{
log.error("无效订单!");
return R.error(500,"无效订单!");
}
// 1.根据订单id获取订单信息
log.warn("=========== 根据订单号,做幂等处理 ===========");
if("order".equals(order.getOrderType())){
BuyOrderEntity orderEntity = buyOrderService.getBaseMapper().selectOne(new QueryWrapper<BuyOrderEntity>().eq("order_sn", wechatEntity.getOrderId()));
BigDecimal realMoney = orderEntity.getRealMoney();
//更新 订单 记录
if (wechatEntity.getTotalAmount().compareTo(realMoney) == 0) {
buyOrderService.updateOrderStatus(Integer.valueOf(order.getUserId()),order.getOrderSn(),"0");
}
}
if("point".equals(order.getOrderType())){
// List<BookBuyConfigEntity> bookBuyConfigEntity = bookBuyConfigService.getBaseMapper().selectList(new QueryWrapper<BookBuyConfigEntity>().eq("price_type_id", ""));
// 插入花生币 变动记录
PayWechatOrderEntity buy_order_id = payWechatOrderService.getBaseMapper().selectOne(new QueryWrapper<PayWechatOrderEntity>().eq("order_sn", order.getOrderSn()));
Integer buyorder= buy_order_id.getBuyOrderId();
BookBuyConfigEntity bookBuyConfigEntity = bookBuyConfigService.getById(buyorder);
String realMoney =bookBuyConfigEntity.getRealMoney();
Integer money = Integer.valueOf(realMoney);
userService.rechargeHSPoint(Integer.valueOf(order.getUserId()),money);
TransactionDetailsEntity transactionDetailsEntity = new TransactionDetailsEntity();
transactionDetailsEntity.setUserId(order.getUserId());
transactionDetailsEntity.setChangeAmount(new BigDecimal(money));
transactionDetailsEntity.setOrderType("充值");
// transactionDetailsEntity.setRelationId(wechatEntity.getId().intValue());
transactionDetailsEntity.setRelationId(buy_order_id.getId().intValue());
transactionDetailsEntity.setRemark("充值");
MyUserEntity user = userService.getById(Integer.valueOf(order.getUserId()));
BigDecimal peanutCoin = user.getPeanutCoin();
transactionDetailsEntity.setUserBalance(peanutCoin);
transactionDetailsEntity.setUserName(user.getNickname());
transactionDetailsEntity.setTel(user.getTel());
transactionDetailsService.save(transactionDetailsEntity);
// 插入 花生币 充值记录
PayPaymentOrderEntity payPaymentOrderEntity = new PayPaymentOrderEntity();
payPaymentOrderEntity.setUserId(Integer.valueOf(order.getUserId()));
payPaymentOrderEntity.setOrderId(String.valueOf(buy_order_id.getId()));
payPaymentOrderEntity.setRealAmount(new BigDecimal(bookBuyConfigEntity.getRealMoney()));
payPaymentOrderEntity.setRechargeAmount(new BigDecimal(bookBuyConfigEntity.getMoney()));
payPaymentOrderEntity.setRechargeChannel(bookBuyConfigEntity.getQudao());
payPaymentOrderEntity.setRechargeStatus("success");
payPaymentOrderEntity.setSuccessTime(new Date());
payPaymentOrderEntity.setUserName(user.getNickname());
payPaymentOrderEntity.setTel(user.getTel());
payPaymentOrderService.save(payPaymentOrderEntity);
buyOrderService.updateOrderStatus(Integer.valueOf(order.getUserId()),order.getOrderSn(),"2");
}
} finally {
// 要主动释放锁
lock.unlock();
}
}
// 成功应答
return R.ok();
}
private Map<String,Object> getNotifyBody(HttpServletRequest request ){
// 处理通知参数
String body = HttpUtils.readData(request);
log.info("zhifu回调参数{}",body);
// 转换为Map
Map<String, Object> bodyMap = JSONObject.parseObject(body, new TypeReference<Map<String, Object>>(){});
// 微信的通知ID通知的唯一ID
String notifyId = bodyMap.get("id").toString();
// 验证签名信息
try {
WechatPayValidator wechatPayValidator
= new WechatPayValidator(wechatPayConfig.getVerifier(), notifyId, body);
if(!wechatPayValidator.validate(request)){
log.error("通知验签失败");
return null;
}
log.info("通知验签成功");
}catch (Exception e) {
}
return bodyMap;
}
}

View File

@@ -0,0 +1,77 @@
package com.peanut.modules.pay.weChatPay.dto;
import cn.hutool.core.date.DateUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.math.BigDecimal;
import java.util.Date;
/**
* @author
* @version 1.0
* @description 微信支付成功回调返回的数据
* @date
*/
@Data
@Slf4j
public class WxchatCallbackSuccessData {
/**
* 商户订单号
*/
private String orderId;
/**
* 微信支付系统生成的订单号
*/
private String transactionId;
/**
* 交易状态
* SUCCESS支付成功
* REFUND转入退款
* NOTPAY未支付
* CLOSED已关闭
* REVOKED已撤销付款码支付
* USERPAYING用户支付中付款码支付
* PAYERROR支付失败(其他原因,如银行返回失败)
*/
private String tradestate;
/**
* 支付完成时间
*/
private Date successTime;
/**
* 交易类型
* JSAPI公众号支付
* NATIVE扫码支付
* APPAPP支付
* MICROPAY付款码支付
* MWEBH5支付
* FACEPAY刷脸支付
*/
private String tradetype;
/**
* 订单总金额
*/
private BigDecimal totalMoney;
public Date getSuccessTime() {
return successTime;
}
public void setSuccessTime(String successTime) {
// Hutool工具包的方法自动识别一些常用格式的日期字符串
this.successTime = DateUtil.parse(successTime);
}
}

View File

@@ -0,0 +1,45 @@
package com.peanut.modules.pay.weChatPay.util;
import org.junit.rules.Verifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
public class HttpUtils {
/**
* 将通知参数转化为字符串
* @param request
* @return
*
* WechatPayValidatorForRequest
*/
public static String readData(HttpServletRequest request) {
BufferedReader br = null;
try {
StringBuilder result = new StringBuilder();
br = request.getReader();
for (String line; (line = br.readLine()) != null; ) {
if (result.length() > 0) {
result.append("\n");
}
result.append(line);
}
return result.toString();
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}}}}

View File

@@ -0,0 +1,150 @@
package com.peanut.modules.pay.weChatPay.util;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.util.EntityUtils;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.DateTimeException;
import java.time.Duration;
import java.time.Instant;
import java.util.Map;
import static com.wechat.pay.contrib.apache.httpclient.constant.WechatPayHttpHeaders.*;
/**
* @Author:
* @Description:
**/
@Slf4j
public class WechatPayValidator {
/**
* 应答超时时间,单位为分钟
*/
private static final long RESPONSE_EXPIRED_MINUTES = 5;
private final Verifier verifier;
private final String requestId;
private final String body;
public WechatPayValidator(Verifier verifier, String requestId, String body) {
this.verifier = verifier;
this.requestId = requestId;
this.body = body;
}
protected static IllegalArgumentException parameterError(String message, Object... args) {
message = String.format(message, args);
return new IllegalArgumentException("parameter error: " + message);
}
protected static IllegalArgumentException verifyFail(String message, Object... args) {
message = String.format(message, args);
return new IllegalArgumentException("signature verify fail: " + message);
}
public final boolean validate(HttpServletRequest request) {
try {
//处理请求参数
validateParameters(request);
//构造验签名串
String message = buildMessage(request);
String serial = request.getHeader(WECHAT_PAY_SERIAL);
String signature = request.getHeader(WECHAT_PAY_SIGNATURE);
//验签
if (!verifier.verify(serial, message.getBytes(StandardCharsets.UTF_8), signature)) {
throw verifyFail("serial=[%s] message=[%s] sign=[%s], request-id=[%s]",
serial, message, signature, requestId);
}
} catch (IllegalArgumentException e) {
log.warn(e.getMessage());
return false;
}
return true;
}
private void validateParameters(HttpServletRequest request) {
// NOTE: ensure HEADER_WECHAT_PAY_TIMESTAMP at last
String[] headers = {WECHAT_PAY_SERIAL, WECHAT_PAY_SIGNATURE, WECHAT_PAY_NONCE, WECHAT_PAY_TIMESTAMP};
String header = null;
for (String headerName : headers) {
header = request.getHeader(headerName);
if (header == null) {
throw parameterError("empty [%s], request-id=[%s]", headerName, requestId);
}
}
//判断请求是否过期
String timestampStr = header;
try {
Instant responseTime = Instant.ofEpochSecond(Long.parseLong(timestampStr));
// 拒绝过期请求
if (Duration.between(responseTime, Instant.now()).abs().toMinutes() >= RESPONSE_EXPIRED_MINUTES) {
throw parameterError("timestamp=[%s] expires, request-id=[%s]", timestampStr, requestId);
}
} catch (DateTimeException | NumberFormatException e) {
throw parameterError("invalid timestamp=[%s], request-id=[%s]", timestampStr, requestId);
}
}
private String buildMessage(HttpServletRequest request) {
String timestamp = request.getHeader(WECHAT_PAY_TIMESTAMP);
String nonce = request.getHeader(WECHAT_PAY_NONCE);
return timestamp + "\n"
+ nonce + "\n"
+ body + "\n";
}
private String getResponseBody(CloseableHttpResponse response) throws IOException {
HttpEntity entity = response.getEntity();
return (entity != null && entity.isRepeatable()) ? EntityUtils.toString(entity) : "";
}
/**
* 对称解密,异步通知的加密数据
* @param resource 加密数据
* @param apiV3Key apiV3密钥
* @param type 1-支付2-退款
* @return
*/
public static Map<String, Object> decryptFromResource(String resource,String apiV3Key,Integer type) {
String msg = type==1?"支付成功":"退款成功";
log.info(msg+",回调通知,密文解密");
try {
//通知数据
Map<String, Object> resourceMap = JSONObject.parseObject(resource, new TypeReference<Map<String, Object>>() {
});
//数据密文
String ciphertext = resourceMap.get("ciphertext").toString();
//随机串
String nonce = resourceMap.get("nonce").toString();
//附加数据
String associatedData = resourceMap.get("associated_data").toString();
log.info("密文: {}", ciphertext);
AesUtil aesUtil = new AesUtil(apiV3Key.getBytes(StandardCharsets.UTF_8));
String resourceStr = aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8),
nonce.getBytes(StandardCharsets.UTF_8),
ciphertext);
log.info(msg+",回调通知,解密结果 明文: {}", resourceStr);
return JSONObject.parseObject(resourceStr, new TypeReference<Map<String, Object>>(){});
}catch (Exception e){
throw new RuntimeException("回调参数,解密失败!");
}
}
}

View File

@@ -0,0 +1,69 @@
/**
* Copyright (c) 2016-2019 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package com.peanut.modules.sys.controller;
import com.peanut.common.utils.PageUtils;
import com.peanut.common.utils.R;
import com.peanut.modules.sys.entity.SysAgreementEntity;
import com.peanut.modules.sys.service.SysAgreementService;
import com.peanut.modules.sys.service.SysLogService;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
/**
* 系统日志
*
* @author Mark sunlightcs@gmail.com
*/
@RestController
@RequestMapping("/sys/agreement")
public class SysAgreementController {
@Autowired
private SysAgreementService sysAgreementService;
/**
* 列表
*/
@ResponseBody
@GetMapping("/list")
public R list(@RequestParam Map<String, Object> params){
PageUtils page = sysAgreementService.queryPage(params);
return R.ok().put("page", page);
}
/**
* 保存
* @param agre
* @return
*/
@PostMapping("/save")
public R save(@RequestBody SysAgreementEntity agre){
sysAgreementService.save(agre);
return R.ok();
}
/**
* 更新
* @param agre
* @return
*/
@PostMapping("/update")
public R update(@RequestBody SysAgreementEntity agre){
sysAgreementService.updateById(agre);
return R.ok();
}
}

View File

@@ -0,0 +1,25 @@
/**
* Copyright (c) 2016-2019 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package com.peanut.modules.sys.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.peanut.modules.sys.entity.SysAgreementEntity;
import com.peanut.modules.sys.entity.SysLogEntity;
import org.apache.ibatis.annotations.Mapper;
/**
* 协议
*
* @author Mark sunlightcs@gmail.com
*/
@Mapper
public interface SysAgreementDao extends BaseMapper<SysAgreementEntity> {
}

View File

@@ -0,0 +1,30 @@
package com.peanut.modules.sys.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("sys_agreement")
public class SysAgreementEntity {
@TableId
private Integer id;
/**
* 标题
*/
private String title;
/**
* 内容
*/
private String content;
/**
* 类型
* 会员member
* 充值: pay
*/
private String type;
}

View File

@@ -0,0 +1,29 @@
/**
* Copyright (c) 2016-2019 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package com.peanut.modules.sys.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.peanut.common.utils.PageUtils;
import com.peanut.modules.sys.entity.SysAgreementEntity;
import com.peanut.modules.sys.entity.SysLogEntity;
import java.util.Map;
/**
* 协议
*
* @author Mark sunlightcs@gmail.com
*/
public interface SysAgreementService extends IService<SysAgreementEntity> {
PageUtils queryPage(Map<String, Object> params);
}

View File

@@ -0,0 +1,42 @@
/**
* Copyright (c) 2016-2019 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package com.peanut.modules.sys.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.peanut.common.utils.PageUtils;
import com.peanut.common.utils.Query;
import com.peanut.modules.sys.dao.SysAgreementDao;
import com.peanut.modules.sys.dao.SysLogDao;
import com.peanut.modules.sys.entity.SysAgreementEntity;
import com.peanut.modules.sys.entity.SysLogEntity;
import com.peanut.modules.sys.service.SysAgreementService;
import com.peanut.modules.sys.service.SysLogService;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;
import java.util.Map;
@Service("sysAgreementService")
public class SysAgreementServiceImpl extends ServiceImpl<SysAgreementDao, SysAgreementEntity> implements SysAgreementService {
@Override
public PageUtils queryPage(Map<String, Object> params) {
String key = (String)params.get("key");
IPage<SysAgreementEntity> page = this.page(
new Query<SysAgreementEntity>().getPage(params),
new QueryWrapper<SysAgreementEntity>().like(StringUtils.isNotBlank(key),"type", key)
);
return new PageUtils(page);
}
}