|
@@ -1,28 +1,14 @@
|
|
|
package com.chelvc.framework.wechat;
|
|
|
|
|
|
-import java.nio.charset.StandardCharsets;
|
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
|
import java.util.Objects;
|
|
|
|
|
|
import com.chelvc.framework.base.context.ApplicationContextHolder;
|
|
|
-import com.chelvc.framework.base.context.RestContextHolder;
|
|
|
-import com.chelvc.framework.common.util.AssertUtils;
|
|
|
-import com.chelvc.framework.common.util.HostUtils;
|
|
|
-import com.chelvc.framework.common.util.NumberUtils;
|
|
|
import com.chelvc.framework.common.util.ObjectUtils;
|
|
|
import com.chelvc.framework.common.util.StringUtils;
|
|
|
-import com.chelvc.framework.wechat.config.WechatProperties;
|
|
|
-import com.chelvc.framework.wechat.context.WechatContextHolder;
|
|
|
-import com.github.wxpay.sdk.WXPayUtil;
|
|
|
import com.google.common.collect.Lists;
|
|
|
-import com.google.common.collect.Maps;
|
|
|
import lombok.NonNull;
|
|
|
-import lombok.extern.slf4j.Slf4j;
|
|
|
-import org.springframework.http.HttpEntity;
|
|
|
-import org.springframework.http.HttpHeaders;
|
|
|
-import org.springframework.http.HttpMethod;
|
|
|
-import org.springframework.http.MediaType;
|
|
|
|
|
|
/**
|
|
|
* 微信支付操作默认实现
|
|
@@ -30,38 +16,32 @@ import org.springframework.http.MediaType;
|
|
|
* @author Woody
|
|
|
* @date 2024/6/19
|
|
|
*/
|
|
|
-@Slf4j
|
|
|
public class DefaultWechatPaymentHandler implements WechatPaymentHandler {
|
|
|
- /**
|
|
|
- * 微信下单接口地址
|
|
|
- */
|
|
|
- private static final String UNIFIEDORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
|
|
|
-
|
|
|
- private final List<WechatProperties.Payment> payments;
|
|
|
+ private final List<WechatPaymentProcessor> processors;
|
|
|
|
|
|
- public DefaultWechatPaymentHandler(@NonNull List<WechatProperties.Payment> payments) {
|
|
|
- this.payments = Lists.newArrayList(payments);
|
|
|
+ public DefaultWechatPaymentHandler(@NonNull List<WechatPaymentProcessor> processors) {
|
|
|
+ this.processors = Lists.newArrayList(processors);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 查找支付配置
|
|
|
+ * 查找支付处理器
|
|
|
*
|
|
|
- * @param name 应用名称
|
|
|
- * @return 支付配置
|
|
|
+ * @param name 处理器名称
|
|
|
+ * @return 支付处理器实例
|
|
|
*/
|
|
|
- private WechatProperties.Payment lookupPayment(String name) {
|
|
|
- if (ObjectUtils.notEmpty(this.payments)) {
|
|
|
- WechatProperties.Payment optional = null;
|
|
|
+ private WechatPaymentProcessor lookupPaymentProcessor(String name) {
|
|
|
+ if (ObjectUtils.notEmpty(this.processors)) {
|
|
|
+ WechatPaymentProcessor optional = null;
|
|
|
String merchant = ApplicationContextHolder.getProperty("wechat.payment." + name + ".merchant");
|
|
|
- for (WechatProperties.Payment payment : this.payments) {
|
|
|
- if (Objects.equals(payment.getName(), name)) {
|
|
|
+ for (WechatPaymentProcessor processor : this.processors) {
|
|
|
+ if (Objects.equals(processor.getName(), name)) {
|
|
|
if (optional == null) {
|
|
|
- optional = payment;
|
|
|
+ optional = processor;
|
|
|
}
|
|
|
|
|
|
// 优先使用指定商户号支付配置
|
|
|
- if (StringUtils.isEmpty(merchant) || Objects.equals(payment.getMchid(), merchant)) {
|
|
|
- return payment;
|
|
|
+ if (StringUtils.isEmpty(merchant) || Objects.equals(processor.getMchid(), merchant)) {
|
|
|
+ return processor;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -71,54 +51,25 @@ public class DefaultWechatPaymentHandler implements WechatPaymentHandler {
|
|
|
return optional;
|
|
|
}
|
|
|
}
|
|
|
- throw new IllegalArgumentException("Not support payment: " + name);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 构建支付参数
|
|
|
- *
|
|
|
- * @param payment 支付配置
|
|
|
- * @param request 支付请求参数
|
|
|
- * @return 支付参数键/值对
|
|
|
- */
|
|
|
- private Map<String, String> buildPaymentParameter(WechatProperties.Payment payment, WechatPayRequest request) {
|
|
|
- String mchid = AssertUtils.nonempty(payment.getMchid(), () -> "Payment mchid is missing");
|
|
|
- String callback = AssertUtils.nonempty(payment.getCallback(), () -> "Payment callback is missing");
|
|
|
- Map<String, String> parameters = Maps.newHashMap();
|
|
|
- parameters.put("appid", payment.getAppid());
|
|
|
- parameters.put("mch_id", mchid);
|
|
|
- parameters.put("body", request.getComment());
|
|
|
- parameters.put("out_trade_no", request.getOrder());
|
|
|
- if (StringUtils.notEmpty(request.getContext())) {
|
|
|
- parameters.put("attach", request.getContext());
|
|
|
- }
|
|
|
- parameters.put("fee_type", "CNY");
|
|
|
- parameters.put("total_fee", NumberUtils.format(NumberUtils.multiply100(request.getAmount()), true));
|
|
|
- parameters.put("spbill_create_ip", HostUtils.LOCAL_ADDRESS);
|
|
|
- parameters.put("notify_url", callback);
|
|
|
- parameters.put("trade_type", payment.getMode().name());
|
|
|
- parameters.put("nonce_str", WXPayUtil.generateNonceStr());
|
|
|
- return parameters;
|
|
|
+ throw new UnsupportedOperationException("Wechat payment processor not found: " + name);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public String sign(@NonNull String name, @NonNull Map<String, String> parameters) {
|
|
|
- WechatProperties.Payment payment = this.lookupPayment(name);
|
|
|
- return WechatContextHolder.sign(payment.getMchkey(), parameters);
|
|
|
+ return this.lookupPaymentProcessor(name).sign(parameters);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public boolean validate(@NonNull String name, @NonNull Map<String, String> parameters) {
|
|
|
- if (ObjectUtils.isEmpty(this.payments)) {
|
|
|
+ if (ObjectUtils.isEmpty(this.processors)) {
|
|
|
return false;
|
|
|
}
|
|
|
String sign = parameters.get("sign");
|
|
|
if (StringUtils.isEmpty(sign)) {
|
|
|
return false;
|
|
|
}
|
|
|
- for (WechatProperties.Payment payment : this.payments) {
|
|
|
- if (Objects.equals(payment.getName(), name)
|
|
|
- && Objects.equals(WechatContextHolder.sign(payment.getMchkey(), parameters), sign)) {
|
|
|
+ for (WechatPaymentProcessor processor : this.processors) {
|
|
|
+ if (Objects.equals(processor.getName(), name) && Objects.equals(processor.sign(parameters), sign)) {
|
|
|
return true;
|
|
|
}
|
|
|
}
|
|
@@ -127,56 +78,6 @@ public class DefaultWechatPaymentHandler implements WechatPaymentHandler {
|
|
|
|
|
|
@Override
|
|
|
public WechatUnifiedOrder unifiedorder(@NonNull String name, @NonNull WechatPayRequest request) {
|
|
|
- // 获取支付配置
|
|
|
- WechatProperties.Payment payment = this.lookupPayment(name);
|
|
|
-
|
|
|
- // 构建请求参数
|
|
|
- Map<String, String> parameters = this.buildPaymentParameter(payment, request);
|
|
|
-
|
|
|
- if (payment.getMode() == PayMode.MWEB) {
|
|
|
- // 如果是H5支付则需要指定scene_info,场景信息
|
|
|
- parameters.put("scene_info", AssertUtils.nonempty(request.getScene(), () -> "Payment scene is missing"));
|
|
|
- } else if (payment.getMode() == PayMode.JSAPI) {
|
|
|
- // 如果是小程序支付则需要指定openid
|
|
|
- parameters.put("openid", AssertUtils.nonempty(request.getOpenid(), () -> "Payment openid is missing"));
|
|
|
- }
|
|
|
-
|
|
|
- // 请求参数签名
|
|
|
- String mchkey = payment.getMchkey();
|
|
|
- parameters.put("sign", WechatContextHolder.sign(mchkey, parameters));
|
|
|
- boolean debug = log.isDebugEnabled();
|
|
|
- if (debug) {
|
|
|
- log.debug("Wechat unifiedorder request: {}", parameters);
|
|
|
- }
|
|
|
-
|
|
|
- // 将请求参数转换成xml请求体
|
|
|
- String body = WechatContextHolder.map2xml(parameters);
|
|
|
-
|
|
|
- // 发起下单请求
|
|
|
- HttpHeaders headers = new HttpHeaders();
|
|
|
- headers.setContentType(MediaType.TEXT_XML);
|
|
|
- HttpEntity<?> entity = new HttpEntity<>(body, headers);
|
|
|
- String response = RestContextHolder.execute(rest -> rest.exchange(
|
|
|
- UNIFIEDORDER_URL, HttpMethod.POST, entity, String.class
|
|
|
- )).getBody();
|
|
|
-
|
|
|
- // 验证相应结果
|
|
|
- Map<String, String> result = ObjectUtils.ifNull(response, WechatContextHolder::xml2map);
|
|
|
- if (debug) {
|
|
|
- log.debug("Wechat unifiedorder response: {}", result);
|
|
|
- }
|
|
|
- if (Objects.isNull(result) || !Objects.equals(result.get("return_code"), "SUCCESS")
|
|
|
- || !Objects.equals(result.get("sign"), WechatContextHolder.sign(mchkey, result))) {
|
|
|
- String message = result == null ? null : result.get("return_msg");
|
|
|
- if (StringUtils.notEmpty(message)) {
|
|
|
- message = new String(message.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
|
|
|
- }
|
|
|
- throw new RuntimeException("Wechat unifiedorder failed: " + message);
|
|
|
- }
|
|
|
-
|
|
|
- // 构建微信统一下单信息
|
|
|
- return WechatUnifiedOrder.builder().mode(payment.getMode()).appid(result.get("appid"))
|
|
|
- .mchid(result.get("mch_id")).nonce(result.get("nonce_str")).prepayid(result.get("prepay_id"))
|
|
|
- .qrcode(result.get("code_url")).redirect(result.get("mweb_url")).build();
|
|
|
+ return this.lookupPaymentProcessor(name).unifiedorder(request);
|
|
|
}
|
|
|
}
|