1. 添加模拟交易接口

2. 完善支付完成通知接口业务逻辑
3. 新增支付完成通知自定义处理业务接口
4. 添加小程序支付签名生成
5. 完善Jsapi支付预处理逻辑
6. 完善支付业务结构封装
This commit is contained in:
wanggeng 2021-08-20 23:14:33 +08:00
parent e263433ec7
commit 226483602d
15 changed files with 1161 additions and 21 deletions

View File

@ -0,0 +1,57 @@
package ink.wgink.module.wechat.controller.app.miniapp;
import ink.wgink.interfaces.consts.ISystemConstant;
import ink.wgink.module.wechat.pojo.pay.v3.PaySign;
import ink.wgink.module.wechat.pojo.pay.v3.jsapi.pojo.PayPlaceOrder;
import ink.wgink.module.wechat.service.pay.v3.jsapi.IJsapiService;
import ink.wgink.pojo.result.ErrorResult;
import ink.wgink.util.OrderUtil;
import io.swagger.annotations.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @ClassName: MiniappPayAppController
* @Description: 小程序支付
* @Author: wanggeng
* @Date: 2021/8/20 5:48 下午
* @Version: 1.0
*/
@Api(tags = ISystemConstant.API_TAGS_APP_PREFIX + "小程序用户")
@RestController
@RequestMapping(ISystemConstant.APP_PREFIX + "/miniapp/pay")
public class MiniappPayAppController {
@Autowired
private IJsapiService jsapiService;
@ApiOperation(value = "下订单测试", notes = "下订单测试接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "token", value = "token", paramType = "header")
})
@ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)})
@PostMapping("save-place-order-demo")
public PaySign savePlaceOrderDemo(@RequestHeader("token") String token) throws Exception {
String atomicOrder = OrderUtil.getAtomicOrder("0001");
System.out.println("订单号:" + atomicOrder);
PayPlaceOrder payPlaceOrder = new PayPlaceOrder();
payPlaceOrder.setDescription("测试商品");
payPlaceOrder.setOutTradeNo(atomicOrder);
payPlaceOrder.setNotifyUrl("https://www.wgink.ink/study/wechat/pay/notice");
PayPlaceOrder.Amount amount = new PayPlaceOrder.Amount();
amount.setTotal(1);
payPlaceOrder.setAmount(amount);
PayPlaceOrder.Payer payer = new PayPlaceOrder.Payer();
payer.setOpenid("");
payPlaceOrder.setPayer(payer);
return jsapiService.placeOrder(payPlaceOrder);
}
}

View File

@ -0,0 +1,43 @@
package ink.wgink.module.wechat.controller.wechat.pay.v3;
import ink.wgink.annotation.CheckRequestBodyAnnotation;
import ink.wgink.common.base.DefaultBaseController;
import ink.wgink.interfaces.consts.ISystemConstant;
import ink.wgink.module.wechat.pojo.pay.v3.PayErrorResponse;
import ink.wgink.module.wechat.pojo.pay.v3.PayNotice;
import ink.wgink.module.wechat.service.pay.v3.IPayService;
import io.swagger.annotations.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @ClassName: PayController
* @Description: 微信支付
* @Author: wanggeng
* @Date: 2021/8/19 9:36 下午
* @Version: 1.0
*/
@Api(tags = ISystemConstant.API_TAGS_WECHAT_PREFIX + "微信公众号")
@RestController
@RequestMapping(ISystemConstant.WECHAT_PREFIX + "/pay")
public class PayController extends DefaultBaseController {
@Autowired
private IPayService payService;
@ApiOperation(value = "通知支付结果", notes = "有微信支付主动发起的支付结果通知接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "Wechatpay-Signature", value = "来自微信的签名值", paramType = "header")
})
@ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = PayErrorResponse.class)})
@PostMapping("notice")
@CheckRequestBodyAnnotation
public void notice(HttpServletRequest request, HttpServletResponse response,
@RequestHeader("Wechatpay-Signature") String wechatpaySignature, @RequestBody PayNotice payNotice) {
payService.notice(request, response, wechatpaySignature, payNotice);
}
}

View File

@ -52,6 +52,10 @@ public class OfficialAccountAccessTokenManager {
this.officialAccountAccessToken = officialAccountAccessToken;
}
public OfficialAccountAccessToken getAccessToken() {
return officialAccountAccessToken;
}
private static class AccessTokenManagerFactory {
public static OfficialAccountAccessTokenManager officialAccountAccessTokenManager = new OfficialAccountAccessTokenManager();
}

View File

@ -0,0 +1,168 @@
package ink.wgink.module.wechat.pojo.pay.v3;
import ink.wgink.annotation.CheckBeanAnnotation;
import ink.wgink.annotation.CheckEmptyAnnotation;
import ink.wgink.annotation.CheckNumberAnnotation;
/**
* @ClassName: PayNotice
* @Description: 支付通知
* @Author: wanggeng
* @Date: 2021/8/20 8:07 上午
* @Version: 1.0
*/
public class PayNotice {
@CheckEmptyAnnotation(name = "通知ID")
private String id;
@CheckEmptyAnnotation(name = "通知创建时间")
private String create_time;
@CheckEmptyAnnotation(name = "通知类型")
private String event_type;
@CheckEmptyAnnotation(name = "通知数据类型")
private String resource_type;
@CheckNumberAnnotation(name = "通知数据")
@CheckBeanAnnotation
private Resource resource;
@CheckEmptyAnnotation(name = "回调摘要")
private String summary;
public String getId() {
return id == null ? "" : id.trim();
}
public void setId(String id) {
this.id = id;
}
public String getCreate_time() {
return create_time == null ? "" : create_time.trim();
}
public void setCreate_time(String create_time) {
this.create_time = create_time;
}
public String getEvent_type() {
return event_type == null ? "" : event_type.trim();
}
public void setEvent_type(String event_type) {
this.event_type = event_type;
}
public String getResource_type() {
return resource_type == null ? "" : resource_type.trim();
}
public void setResource_type(String resource_type) {
this.resource_type = resource_type;
}
public Resource getResource() {
return resource;
}
public void setResource(Resource resource) {
this.resource = resource;
}
public String getSummary() {
return summary == null ? "" : summary.trim();
}
public void setSummary(String summary) {
this.summary = summary;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("{");
sb.append("\"id\":\"")
.append(id).append('\"');
sb.append(",\"create_time\":\"")
.append(create_time).append('\"');
sb.append(",\"event_type\":\"")
.append(event_type).append('\"');
sb.append(",\"resource_type\":\"")
.append(resource_type).append('\"');
sb.append(",\"resource\":")
.append(resource);
sb.append(",\"summary\":\"")
.append(summary).append('\"');
sb.append('}');
return sb.toString();
}
public static class Resource {
@CheckEmptyAnnotation(name = "加密算法类型")
private String algorithm;
@CheckEmptyAnnotation(name = "数据密文")
private String ciphertext;
/**
* 附加数据
*/
private String original_type;
@CheckEmptyAnnotation(name = "原始类型")
private String associated_data;
@CheckEmptyAnnotation(name = "随机")
private String nonce;
public String getAlgorithm() {
return algorithm == null ? "" : algorithm.trim();
}
public void setAlgorithm(String algorithm) {
this.algorithm = algorithm;
}
public String getCiphertext() {
return ciphertext == null ? "" : ciphertext.trim();
}
public void setCiphertext(String ciphertext) {
this.ciphertext = ciphertext;
}
public String getOriginal_type() {
return original_type == null ? "" : original_type.trim();
}
public void setOriginal_type(String original_type) {
this.original_type = original_type;
}
public String getAssociated_data() {
return associated_data == null ? "" : associated_data.trim();
}
public void setAssociated_data(String associated_data) {
this.associated_data = associated_data;
}
public String getNonce() {
return nonce == null ? "" : nonce.trim();
}
public void setNonce(String nonce) {
this.nonce = nonce;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("{");
sb.append("\"algorithm\":\"")
.append(algorithm).append('\"');
sb.append(",\"ciphertext\":\"")
.append(ciphertext).append('\"');
sb.append(",\"original_type\":\"")
.append(original_type).append('\"');
sb.append(",\"associated_data\":\"")
.append(associated_data).append('\"');
sb.append(",\"nonce\":\"")
.append(nonce).append('\"');
sb.append('}');
return sb.toString();
}
}
}

View File

@ -0,0 +1,590 @@
package ink.wgink.module.wechat.pojo.pay.v3;
/**
* @ClassName: PayNoticeCiphertext
* @Description: 支付通知密文
* @Author: wanggeng
* @Date: 2021/8/20 3:02 下午
* @Version: 1.0
*/
public class PayNoticeCiphertext {
/**
* 应用ID
*/
private String appId;
/**
* 商户号
*/
private String mchid;
/**
* 商户订单号
*/
private String out_trade_no;
/**
* 微信支付订单号
*/
private String transaction_id;
/**
* 交易类型
*/
private String trade_type;
/**
* 交易状态
*/
private String trade_state;
/**
* 交易状态描述
*/
private String trade_state_desc;
/**
* 付款银行
*/
private String bank_type;
/**
* 附加数据
*/
private String attach;
/**
* 支付完成时间
*/
private String success_time;
/**
* 支付者
*/
private Payer payer;
/**
* 订单金额
*/
private Amount amount;
/**
* 场景信息
*/
private SceneInfo scene_info;
/**
* 优惠功能
*/
private PromotionDetail promotion_detail;
public String getAppId() {
return appId == null ? "" : appId.trim();
}
public void setAppId(String appId) {
this.appId = appId;
}
public String getMchid() {
return mchid == null ? "" : mchid.trim();
}
public void setMchid(String mchid) {
this.mchid = mchid;
}
public String getOut_trade_no() {
return out_trade_no == null ? "" : out_trade_no.trim();
}
public void setOut_trade_no(String out_trade_no) {
this.out_trade_no = out_trade_no;
}
public String getTransaction_id() {
return transaction_id == null ? "" : transaction_id.trim();
}
public void setTransaction_id(String transaction_id) {
this.transaction_id = transaction_id;
}
public String getTrade_type() {
return trade_type == null ? "" : trade_type.trim();
}
public void setTrade_type(String trade_type) {
this.trade_type = trade_type;
}
public String getTrade_state() {
return trade_state == null ? "" : trade_state.trim();
}
public void setTrade_state(String trade_state) {
this.trade_state = trade_state;
}
public String getTrade_state_desc() {
return trade_state_desc == null ? "" : trade_state_desc.trim();
}
public void setTrade_state_desc(String trade_state_desc) {
this.trade_state_desc = trade_state_desc;
}
public String getBank_type() {
return bank_type == null ? "" : bank_type.trim();
}
public void setBank_type(String bank_type) {
this.bank_type = bank_type;
}
public String getAttach() {
return attach == null ? "" : attach.trim();
}
public void setAttach(String attach) {
this.attach = attach;
}
public String getSuccess_time() {
return success_time == null ? "" : success_time.trim();
}
public void setSuccess_time(String success_time) {
this.success_time = success_time;
}
public Payer getPayer() {
return payer;
}
public void setPayer(Payer payer) {
this.payer = payer;
}
public Amount getAmount() {
return amount;
}
public void setAmount(Amount amount) {
this.amount = amount;
}
public SceneInfo getScene_info() {
return scene_info;
}
public void setScene_info(SceneInfo scene_info) {
this.scene_info = scene_info;
}
public PromotionDetail getPromotion_detail() {
return promotion_detail;
}
public void setPromotion_detail(PromotionDetail promotion_detail) {
this.promotion_detail = promotion_detail;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("{");
sb.append("\"appId\":\"")
.append(appId).append('\"');
sb.append(",\"mchid\":\"")
.append(mchid).append('\"');
sb.append(",\"out_trade_no\":\"")
.append(out_trade_no).append('\"');
sb.append(",\"transaction_id\":\"")
.append(transaction_id).append('\"');
sb.append(",\"trade_type\":\"")
.append(trade_type).append('\"');
sb.append(",\"trade_state\":\"")
.append(trade_state).append('\"');
sb.append(",\"trade_state_desc\":\"")
.append(trade_state_desc).append('\"');
sb.append(",\"bank_type\":\"")
.append(bank_type).append('\"');
sb.append(",\"attach\":\"")
.append(attach).append('\"');
sb.append(",\"success_time\":\"")
.append(success_time).append('\"');
sb.append(",\"payer\":")
.append(payer);
sb.append(",\"amount\":")
.append(amount);
sb.append(",\"scene_info\":")
.append(scene_info);
sb.append(",\"promotion_detail\":")
.append(promotion_detail);
sb.append('}');
return sb.toString();
}
/**
* 支付者
*/
public static class Payer {
/**
* 用户标识
*/
private String openid;
public String getOpenid() {
return openid == null ? "" : openid.trim();
}
public void setOpenid(String openid) {
this.openid = openid;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("{");
sb.append("\"openid\":\"")
.append(openid).append('\"');
sb.append('}');
return sb.toString();
}
}
/**
* 订单金额
*/
public static class Amount {
/**
* 总金额
*/
private int total;
/**
* 用户支付金额
*/
private int payer_total;
/**
* 货币类型
*/
private String currency;
/**
* 用户支付币种
*/
private String payer_currency;
public int getTotal() {
return total;
}
public void setTotal(int total) {
this.total = total;
}
public int getPayer_total() {
return payer_total;
}
public void setPayer_total(int payer_total) {
this.payer_total = payer_total;
}
public String getCurrency() {
return currency == null ? "" : currency.trim();
}
public void setCurrency(String currency) {
this.currency = currency;
}
public String getPayer_currency() {
return payer_currency == null ? "" : payer_currency.trim();
}
public void setPayer_currency(String payer_currency) {
this.payer_currency = payer_currency;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("{");
sb.append("\"total\":")
.append(total);
sb.append(",\"payer_total\":")
.append(payer_total);
sb.append(",\"currency\":\"")
.append(currency).append('\"');
sb.append(",\"payer_currency\":\"")
.append(payer_currency).append('\"');
sb.append('}');
return sb.toString();
}
}
/**
* 场景信息
*/
public static class SceneInfo {
/**
* 商户端设备号
*/
private String device_id;
public String getDevice_id() {
return device_id == null ? "" : device_id.trim();
}
public void setDevice_id(String device_id) {
this.device_id = device_id;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("{");
sb.append("\"device_id\":\"")
.append(device_id).append('\"');
sb.append('}');
return sb.toString();
}
}
/**
* 优惠功能
*/
public static class PromotionDetail {
/**
* 券ID
*/
private String coupon_id;
/**
* 优惠名称
*/
private String name;
/**
* 优惠范围
*/
private String scope;
/**
* 优惠类型
*/
private String type;
/**
* 优惠券面额
*/
private Integer amount;
/**
* 活动ID
*/
private String stock_id;
/**
* 微信出资
*/
private Integer wechatpay_contribute;
/**
* 商户出资
*/
private Integer merchant_contribute;
/**
* 其他出资
*/
private Integer other_contribute;
/**
* 优惠币种
*/
private String currency;
/**
* 单品列表
*/
private GoodsDetail goods_detail;
public String getCoupon_id() {
return coupon_id == null ? "" : coupon_id.trim();
}
public void setCoupon_id(String coupon_id) {
this.coupon_id = coupon_id;
}
public String getName() {
return name == null ? "" : name.trim();
}
public void setName(String name) {
this.name = name;
}
public String getScope() {
return scope == null ? "" : scope.trim();
}
public void setScope(String scope) {
this.scope = scope;
}
public String getType() {
return type == null ? "" : type.trim();
}
public void setType(String type) {
this.type = type;
}
public Integer getAmount() {
return amount == null ? 0 : amount;
}
public void setAmount(Integer amount) {
this.amount = amount;
}
public String getStock_id() {
return stock_id == null ? "" : stock_id.trim();
}
public void setStock_id(String stock_id) {
this.stock_id = stock_id;
}
public Integer getWechatpay_contribute() {
return wechatpay_contribute == null ? 0 : wechatpay_contribute;
}
public void setWechatpay_contribute(Integer wechatpay_contribute) {
this.wechatpay_contribute = wechatpay_contribute;
}
public Integer getMerchant_contribute() {
return merchant_contribute == null ? 0 : merchant_contribute;
}
public void setMerchant_contribute(Integer merchant_contribute) {
this.merchant_contribute = merchant_contribute;
}
public Integer getOther_contribute() {
return other_contribute == null ? 0 : other_contribute;
}
public void setOther_contribute(Integer other_contribute) {
this.other_contribute = other_contribute;
}
public String getCurrency() {
return currency == null ? "" : currency.trim();
}
public void setCurrency(String currency) {
this.currency = currency;
}
public GoodsDetail getGoods_detail() {
return goods_detail;
}
public void setGoods_detail(GoodsDetail goods_detail) {
this.goods_detail = goods_detail;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("{");
sb.append("\"coupon_id\":\"")
.append(coupon_id).append('\"');
sb.append(",\"name\":\"")
.append(name).append('\"');
sb.append(",\"scope\":\"")
.append(scope).append('\"');
sb.append(",\"type\":\"")
.append(type).append('\"');
sb.append(",\"amount\":")
.append(amount);
sb.append(",\"stock_id\":\"")
.append(stock_id).append('\"');
sb.append(",\"wechatpay_contribute\":")
.append(wechatpay_contribute);
sb.append(",\"merchant_contribute\":")
.append(merchant_contribute);
sb.append(",\"other_contribute\":")
.append(other_contribute);
sb.append(",\"currency\":\"")
.append(currency).append('\"');
sb.append(",\"goods_detail\":")
.append(goods_detail);
sb.append('}');
return sb.toString();
}
/**
* 单品列表
*/
public static class GoodsDetail {
/**
* 商品编码
*/
private String goods_id;
/**
* 商品数量
*/
private Integer quantity;
/**
* 商品单价
*/
private Integer unit_price;
/**
* 商品优惠金额
*/
private Integer discount_amount;
/**
* 商品备注
*/
private String goods_remark;
public String getGoods_id() {
return goods_id == null ? "" : goods_id.trim();
}
public void setGoods_id(String goods_id) {
this.goods_id = goods_id;
}
public Integer getQuantity() {
return quantity == null ? 0 : quantity;
}
public void setQuantity(Integer quantity) {
this.quantity = quantity;
}
public Integer getUnit_price() {
return unit_price == null ? 0 : unit_price;
}
public void setUnit_price(Integer unit_price) {
this.unit_price = unit_price;
}
public Integer getDiscount_amount() {
return discount_amount == null ? 0 : discount_amount;
}
public void setDiscount_amount(Integer discount_amount) {
this.discount_amount = discount_amount;
}
public String getGoods_remark() {
return goods_remark == null ? "" : goods_remark.trim();
}
public void setGoods_remark(String goods_remark) {
this.goods_remark = goods_remark;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("{");
sb.append("\"goods_id\":\"")
.append(goods_id).append('\"');
sb.append(",\"quantity\":")
.append(quantity);
sb.append(",\"unit_price\":")
.append(unit_price);
sb.append(",\"discount_amount\":")
.append(discount_amount);
sb.append(",\"goods_remark\":\"")
.append(goods_remark).append('\"');
sb.append('}');
return sb.toString();
}
}
}
}

View File

@ -0,0 +1,77 @@
package ink.wgink.module.wechat.pojo.pay.v3;
/**
* @ClassName: PaySign
* @Description: 支付签名
* @Author: wanggeng
* @Date: 2021/8/20 5:41 下午
* @Version: 1.0
*/
public class PaySign {
private String appId;
private String timestamp;
private String nonceStr;
/**
* 预支付交易会话标识预支付交易会话标识用于后续接口调用中使用该值有效期为2小时
*/
private String prepayId;
private String paySign;
public String getAppId() {
return appId == null ? "" : appId.trim();
}
public void setAppId(String appId) {
this.appId = appId;
}
public String getTimestamp() {
return timestamp == null ? "" : timestamp.trim();
}
public void setTimestamp(String timestamp) {
this.timestamp = timestamp;
}
public String getNonceStr() {
return nonceStr == null ? "" : nonceStr.trim();
}
public void setNonceStr(String nonceStr) {
this.nonceStr = nonceStr;
}
public String getPrepayId() {
return prepayId == null ? "" : prepayId.trim();
}
public void setPrepayId(String prepayId) {
this.prepayId = prepayId;
}
public String getPaySign() {
return paySign == null ? "" : paySign.trim();
}
public void setPaySign(String paySign) {
this.paySign = paySign;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("{");
sb.append("\"appId\":\"")
.append(appId).append('\"');
sb.append(",\"timestamp\":\"")
.append(timestamp).append('\"');
sb.append(",\"nonceStr\":\"")
.append(nonceStr).append('\"');
sb.append(",\"prepayId\":\"")
.append(prepayId).append('\"');
sb.append(",\"paySign\":\"")
.append(paySign).append('\"');
sb.append('}');
return sb.toString();
}
}

View File

@ -30,14 +30,15 @@ public class PlaceOrderPayRequestImpl extends AbstractPayRequest<PayPlaceOrder,
bodyObject.put("notify_url", payPlaceOrder.getNotifyUrl());
bodyObject.put("goods_tag", payPlaceOrder.getGoodsTag());
bodyObject.put("amount", getAmountJsonObject(payPlaceOrder.getAmount()));
bodyObject.put("payer", getPayer(payPlaceOrder.getPayer()));
if (payPlaceOrder.getDetail() != null) {
bodyObject.put("detail", payPlaceOrder.getDetail());
bodyObject.put("detail", getDetail(payPlaceOrder.getDetail()));
}
if (payPlaceOrder.getSceneInfo() != null) {
bodyObject.put("scene_info", payPlaceOrder.getSceneInfo());
bodyObject.put("scene_info", getSceneInfo(payPlaceOrder.getSceneInfo()));
}
if (payPlaceOrder.getSettleInfo() != null) {
bodyObject.put("settle_info", payPlaceOrder.getSettleInfo());
bodyObject.put("settle_info", getSettleInfo(payPlaceOrder.getSettleInfo()));
}
return bodyObject.toJSONString();
}

View File

@ -0,0 +1,22 @@
package ink.wgink.module.wechat.service.pay.v3;
import ink.wgink.module.wechat.pojo.pay.v3.PayNoticeCiphertext;
/**
* @ClassName: IPayNoticeService
* @Description: 支付通知业务完成支付后微信支付发起异步通知实现该类完成业务处理
* @Author: wanggeng
* @Date: 2021/8/20 4:45 下午
* @Version: 1.0
*/
public interface IPayNoticeService {
/**
* 处理支付通知结果该方法在所有校验通过加密文本解密之后调用
*
* @param payNoticeCiphertext
* @throws Exception
*/
void handle(PayNoticeCiphertext payNoticeCiphertext) throws Exception;
}

View File

@ -0,0 +1,26 @@
package ink.wgink.module.wechat.service.pay.v3;
import ink.wgink.module.wechat.pojo.pay.v3.PayNotice;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @ClassName: IPayService
* @Description: 支付
* @Author: wanggeng
* @Date: 2021/8/20 8:15 上午
* @Version: 1.0
*/
public interface IPayService {
/**
* 支付通知
*
* @param request
* @param response
* @param wechatpaySignature
* @param payNotice
*/
void notice(HttpServletRequest request, HttpServletResponse response, String wechatpaySignature, PayNotice payNotice);
}

View File

@ -0,0 +1,107 @@
package ink.wgink.module.wechat.service.pay.v3.impl;
import com.alibaba.fastjson.JSONObject;
import ink.wgink.common.base.DefaultBaseService;
import ink.wgink.module.wechat.enums.PayErrorMsgEnum;
import ink.wgink.module.wechat.pojo.pay.v3.PayErrorResponse;
import ink.wgink.module.wechat.pojo.pay.v3.PayNotice;
import ink.wgink.module.wechat.pojo.pay.v3.PayNoticeCiphertext;
import ink.wgink.module.wechat.request.pay.v3.AbstractPayRequest;
import ink.wgink.module.wechat.service.pay.v3.IPayNoticeService;
import ink.wgink.module.wechat.service.pay.v3.IPayService;
import ink.wgink.module.wechat.utils.pay.PayVerifyUtil;
import ink.wgink.properties.wechat.pay.v3.PayProperties;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
/**
* @ClassName: IPayServiceImpl
* @Description: 支付
* @Author: wanggeng
* @Date: 2021/8/20 8:15 上午
* @Version: 1.0
*/
@Service
public class IPayServiceImpl extends DefaultBaseService implements IPayService {
@Autowired
private PayProperties payProperties;
@Autowired
private IPayNoticeService payNoticeService;
@Override
public void notice(HttpServletRequest request, HttpServletResponse response, String wechatpaySignature, PayNotice payNotice) {
String nonce = payNotice.getResource().getNonce();
DateTime createTimeDateTime = DateTime.parse(payNotice.getCreate_time());
long millis = createTimeDateTime.getMillis();
String payNoticeJsonString = JSONObject.toJSONString(payNotice);
try {
LOG.debug("验证签名");
boolean checkVerify = PayVerifyUtil.verifySignature(wechatpaySignature, payNoticeJsonString, nonce, String.valueOf(millis), new FileInputStream(payProperties.getCertificatePath()));
if (!checkVerify) {
errorResult(response, PayErrorMsgEnum.SIGN_ERROR_401);
return;
}
LOG.debug("解密内容");
PayNotice.Resource resource = payNotice.getResource();
AbstractPayRequest.AesUtil aesUtil = new AbstractPayRequest.AesUtil(payProperties.getApiV3Secretkey().getBytes(StandardCharsets.UTF_8));
String payNoticeCiphertextJsonString = aesUtil.decryptToString(resource.getAssociated_data().getBytes(StandardCharsets.UTF_8), resource.getNonce().getBytes(StandardCharsets.UTF_8), resource.getCiphertext());
PayNoticeCiphertext payNoticeCiphertext = JSONObject.parseObject(payNoticeCiphertextJsonString, PayNoticeCiphertext.class);
payNoticeService.handle(payNoticeCiphertext);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
errorResult(response, PayErrorMsgEnum.ERROR_500);
return;
}
successResult(response);
}
/**
* 错误结果
*
* @param payErrorMsgEnum
*/
private void errorResult(HttpServletResponse response, PayErrorMsgEnum payErrorMsgEnum) {
PayErrorResponse payErrorResponse = new PayErrorResponse();
payErrorResponse.setCode(payErrorMsgEnum.getErrorCode());
payErrorResponse.setMessage(payErrorMsgEnum.getSummary());
response.setContentType("application/json; charset=UTF-8");
response.setStatus(payErrorMsgEnum.getCode());
try (PrintWriter writer = response.getWriter()) {
writer.println(JSONObject.toJSONString(payErrorResponse));
writer.flush();
} catch (IOException e) {
LOG.error(e.getMessage(), e);
}
}
/**
* 成功结果
*/
private void successResult(HttpServletResponse response) {
response.setContentType("application/json; charset=UTF-8");
response.setStatus(HttpStatus.OK.value());
JSONObject successJsonObject = new JSONObject();
successJsonObject.put("code", "SUCCESS");
successJsonObject.put("message", "成功");
try (PrintWriter writer = response.getWriter()) {
writer.println(successJsonObject.toJSONString());
writer.flush();
} catch (IOException e) {
LOG.error(e.getMessage(), e);
}
}
}

View File

@ -1,5 +1,6 @@
package ink.wgink.module.wechat.service.pay.v3.jsapi;
import ink.wgink.module.wechat.pojo.pay.v3.PaySign;
import ink.wgink.module.wechat.pojo.pay.v3.jsapi.pojo.PayPlaceOrder;
/**
@ -17,6 +18,6 @@ public interface IJsapiService {
* @param payPlaceOrder
* @return 预支付交易会话标识预支付交易会话标识用于后续接口调用中使用该值有效期为2小时
*/
String placeOrder(PayPlaceOrder payPlaceOrder) throws Exception;
PaySign placeOrder(PayPlaceOrder payPlaceOrder) throws Exception;
}

View File

@ -2,13 +2,18 @@ package ink.wgink.module.wechat.service.pay.v3.jsapi.impl;
import ink.wgink.common.base.DefaultBaseService;
import ink.wgink.module.wechat.enums.PayAuthorizationTypeEnum;
import ink.wgink.module.wechat.pojo.pay.v3.PaySign;
import ink.wgink.module.wechat.pojo.pay.v3.jsapi.pojo.PayPlaceOrder;
import ink.wgink.module.wechat.request.pay.v3.jsapi.PlaceOrderPayRequestImpl;
import ink.wgink.module.wechat.pojo.pay.v3.jsapi.pojo.PayPrepay;
import ink.wgink.module.wechat.request.pay.v3.jsapi.PlaceOrderPayRequestImpl;
import ink.wgink.module.wechat.service.pay.v3.jsapi.IJsapiService;
import ink.wgink.module.wechat.utils.pay.PayCertificateUtil;
import ink.wgink.module.wechat.utils.pay.PayPrivateKeyUtil;
import ink.wgink.properties.wechat.miniapp.MiniappProperties;
import ink.wgink.properties.wechat.pay.v3.PayProperties;
import ink.wgink.util.BeanPropertyCheckUtil;
import ink.wgink.util.UUIDUtil;
import ink.wgink.util.string.WStringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestMethod;
@ -28,22 +33,59 @@ public class JsapiServiceImpl extends DefaultBaseService implements IJsapiServic
@Autowired
private PayProperties payProperties;
@Autowired
private MiniappProperties miniappProperties;
@Override
public String placeOrder(PayPlaceOrder payPlaceOrder) throws Exception {
public PaySign placeOrder(PayPlaceOrder payPlaceOrder) throws Exception {
payPlaceOrder.setAppid(miniappProperties.getAppKey());
payPlaceOrder.setMchid(payProperties.getMchid());
BeanPropertyCheckUtil.checkField(payPlaceOrder);
String urlSuffix = "/v3/pay/transactions/jsapi";
String url = "https://api.mch.weixin.qq.com" + urlSuffix;
// 获得证书
LOG.debug("获得证书");
X509Certificate certificate = PayCertificateUtil.getCertificate(new FileInputStream(payProperties.getCertificatePath()));
String serialNumber = certificate.getSerialNumber().toString(16);
LOG.debug("调用微信支付,发起预支付");
PlaceOrderPayRequestImpl placeOrderPayRequest = new PlaceOrderPayRequestImpl();
String body = placeOrderPayRequest.bodyJsonString(payPlaceOrder);
String authorization = placeOrderPayRequest.getAuthorization(RequestMethod.POST, urlSuffix, serialNumber, payProperties, body, PayAuthorizationTypeEnum.WECHATPAY2_SHA256_RSA2048);
// 请求获得结果
LOG.debug("请求获得结果");
PayPrepay payPrepay = placeOrderPayRequest.post(url, authorization, serialNumber, body);
return payPrepay.getPrepayId();
LOG.debug("生成返回结果");
long timestamp = System.currentTimeMillis();
String nonceStr = WStringUtil.randomSubStr(UUIDUtil.get32UUID(), 10).toUpperCase();
PaySign paySign = new PaySign();
paySign.setAppId(miniappProperties.getAppKey());
paySign.setPrepayId(payPrepay.getPrepayId());
paySign.setNonceStr(nonceStr);
paySign.setTimestamp(String.valueOf(timestamp));
paySign.setPaySign(getPaySign(timestamp, nonceStr, payPrepay.getPrepayId()));
return paySign;
}
/**
* 获得小程序签名
*
* @param timestamp
* @param nonceStr
* @param prepayId
* @return
* @throws Exception
*/
private String getPaySign(long timestamp, String nonceStr, String prepayId) throws Exception {
StringBuilder signBS = new StringBuilder();
signBS.append(miniappProperties.getAppKey()).append("\n")
.append(timestamp).append("\n")
.append(nonceStr).append("\n")
.append("prepay_id=").append(prepayId).append("\n");
String key = PayPrivateKeyUtil.getPrivateKey(payProperties.getKeyPath());
// 生成签名
return PayPrivateKeyUtil.encryptByPrivateKey(signBS.toString(), key);
}
}

View File

@ -2,7 +2,6 @@ package ink.wgink.module.wechat.startup;
import ink.wgink.module.wechat.manager.MiniappManager;
import ink.wgink.module.wechat.manager.OfficialAccountAccessTokenManager;
import ink.wgink.module.wechat.manager.pay.v3.PayManager;
import ink.wgink.properties.wechat.miniapp.MiniappProperties;
import ink.wgink.properties.wechat.official.account.OfficialAccountProperties;
import ink.wgink.properties.wechat.pay.v3.PayProperties;
@ -36,9 +35,6 @@ public class WechatStartUp implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
if (payProperties.getActive()) {
PayManager.getInstance().setX509Certificate(payProperties.getCertificatePath());
}
new Thread(() -> {
refreshOfficialAccountAccessToken();
}).start();

View File

@ -16,9 +16,6 @@ import java.util.*;
*/
public class PaySignAuthorizationUtil {
// 商户号1609461201
// APIv3秘钥_TSKJ_0471_TSkj_0471_tsKJ_0471__
/**
* @param requestMethod 请求方法
* @param url 请求接口

View File

@ -19,11 +19,11 @@ public class PayVerifyUtil {
/**
* 验证签名
*
* @param signature
* @param body
* @param nonce
* @param timestamp
* @param certInputStream
* @param signature 待验证的签名
* @param body 应答主体
* @param nonce 随机串
* @param timestamp 时间戳
* @param certInputStream 微信支付平台证书输入流
* @return
* @throws Exception
*/
@ -35,6 +35,15 @@ public class PayVerifyUtil {
return checkByPublicKey(buildSignMessage, signature, publicKey);
}
/**
* 公钥验证签名
*
* @param data 需要加密的数据
* @param sign 签名
* @param publicKey 公钥
* @return
* @throws Exception
*/
public static boolean checkByPublicKey(String data, String sign, PublicKey publicKey) throws Exception {
java.security.Signature signature = java.security.Signature.getInstance("SHA256WithRSA");
signature.initVerify(publicKey);