| | |
| | | import com.alibaba.fastjson2.JSON; |
| | | import com.alibaba.fastjson2.JSONObject; |
| | | import com.ruoyi.common.core.utils.StringUtils; |
| | | import com.ruoyi.other.util.UUIDUtil; |
| | | import com.ruoyi.other.util.payment.model.*; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.net.InetAddress; |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.*; |
| | | |
| | | /** |
| | |
| | | public class PaymentUtil { |
| | | |
| | | //微信公众号、微信小程序、微信 APP+/H5、云微小程序支付 |
| | | private static final String appId = "wxdeed472c98e42a54"; |
| | | private static final String appId = "wx049faf9c5234f31c"; |
| | | /** |
| | | * 商户密钥 |
| | | */ |
| | | private static final String key = "925899fcc374430f9e4b4ba3db05b448"; |
| | | private static final String key = "ss369875124965782f148539657826321"; |
| | | /** |
| | | * 商户号 |
| | | */ |
| | | private static final String merchantNo = "888122600004175"; |
| | | private static final String merchantNo = "1717539630"; |
| | | /** |
| | | * 平台-报备商户号 |
| | | */ |
| | | private static final String sysTradeMerchantNo = "777168500885852"; |
| | | private static final String sysTradeMerchantNo = ""; |
| | | /** |
| | | * 支付回调地址 |
| | | */ |
| | | private static final String callbackUrl = "https://www.qijisheng.top"; |
| | | private static final String callbackUrl = "https://221.182.45.100:8084"; |
| | | |
| | | |
| | | /** |
| | |
| | | UniPayResult uniPayResult = JSON.parseObject(execute.body(), UniPayResult.class); |
| | | return uniPayResult; |
| | | } |
| | | /** |
| | | * native支付 |
| | | * @param orderNo 商户订单号 |
| | | * @param amount 订单金额 |
| | | * @param notifyUrl 服务器异步通知地址 |
| | | * @return |
| | | */ |
| | | public static String nativePay(String orderNo, BigDecimal amount, String notifyUrl) throws Exception { |
| | | int totalFee = amount.multiply(new BigDecimal("100")).intValue(); |
| | | |
| | | String url = "https://api.mch.weixin.qq.com/pay/unifiedorder"; |
| | | |
| | | // 构建 XML 请求体 |
| | | Map<String, Object> params = new HashMap<>(); |
| | | params.put("appid", appId); |
| | | params.put("mch_id", merchantNo); |
| | | params.put("nonce_str", UUIDUtil.getRandomCode(16)); |
| | | params.put("body", "积分充值"); |
| | | params.put("out_trade_no", orderNo); |
| | | params.put("total_fee", totalFee); |
| | | // params.put("spbill_create_ip", InetAddress.getLocalHost().getHostAddress()); |
| | | params.put("spbill_create_ip", "221.182.45.100"); |
| | | params.put("notify_url", "https://221.182.45.100:8084/undif"); |
| | | params.put("trade_type", "NATIVE"); |
| | | params.put("product_id", "1"); |
| | | |
| | | String sign = sign1(JSONObject.from(params)); // 使用原来的 sign 方法 |
| | | params.put("sign", sign); |
| | | |
| | | String xmlBody = mapToXml(params); // 转换为 XML 字符串 |
| | | |
| | | // 发送请求 |
| | | HttpRequest post = HttpUtil.createPost(url); |
| | | post.header(Header.CONTENT_TYPE, "application/xml"); |
| | | post.body(xmlBody); // 发送原始 XML 字符串 |
| | | |
| | | log.info("Native支付接口请求参数:" + xmlBody); |
| | | HttpResponse execute = post.execute(); |
| | | log.info("Native支付接口请求响应:" + execute.body()); |
| | | |
| | | if (execute.getStatus() != 200) { |
| | | log.error("Native支付接口异常:" + execute.body()); |
| | | return null; |
| | | } |
| | | return execute.body(); |
| | | } |
| | | private static String mapToXml(Map<String, Object> map) { |
| | | StringBuilder sb = new StringBuilder(); |
| | | sb.append("<xml>"); |
| | | for (Map.Entry<String, Object> entry : map.entrySet()) { |
| | | String key = entry.getKey(); |
| | | Object value = entry.getValue(); |
| | | if (value != null && !value.toString().isEmpty()) { |
| | | sb.append("<").append(key).append(">"); |
| | | sb.append(value); |
| | | sb.append("</").append(key).append(">"); |
| | | } |
| | | } |
| | | sb.append("</xml>"); |
| | | return sb.toString(); |
| | | } |
| | | public static String sign1(JSONObject body) { |
| | | Set<Map.Entry<String, Object>> entries = body.entrySet(); |
| | | List<Map.Entry<String, Object>> infoIds = new ArrayList<>(entries); |
| | | |
| | | // 排除 sign 字段本身 |
| | | infoIds.removeIf(entry -> "sign".equals(entry.getKey())); |
| | | |
| | | // 按 ASCII 顺序排序 |
| | | infoIds.sort(Map.Entry.comparingByKey()); |
| | | |
| | | StringBuilder sb = new StringBuilder(); |
| | | for (Map.Entry<String, Object> entry : infoIds) { |
| | | Object val = entry.getValue(); |
| | | if (val != null && !val.toString().trim().isEmpty()) { |
| | | sb.append(entry.getKey()).append("=").append(val).append("&"); |
| | | } |
| | | } |
| | | |
| | | // 最后拼接 &key=商户密钥 |
| | | sb.append("key=").append(key); |
| | | |
| | | String stringSignTemp = sb.toString(); |
| | | log.info("待签名串:{}", stringSignTemp); |
| | | |
| | | // 使用 MD5 加密 |
| | | return MD5AndKL.MD5(stringSignTemp); |
| | | } |
| | | |
| | | /** |
| | | * 微信下单的签名算法 |
| | | * |
| | | * @param map |
| | | * @return |
| | | */ |
| | | private String weixinSignature(Map<String, Object> map) { |
| | | try { |
| | | Set<Map.Entry<String, Object>> entries = map.entrySet(); |
| | | List<Map.Entry<String, Object>> infoIds = new ArrayList<Map.Entry<String, Object>>(entries); |
| | | // 对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序) |
| | | Collections.sort(infoIds, new Comparator<Map.Entry<String, Object>>() { |
| | | public int compare(Map.Entry<String, Object> o1, Map.Entry<String, Object> o2) { |
| | | return (o1.getKey()).toString().compareTo(o2.getKey()); |
| | | } |
| | | }); |
| | | // 构造签名键值对的格式 |
| | | StringBuilder sb = new StringBuilder(); |
| | | for (Map.Entry<String, Object> item : infoIds) { |
| | | if (item.getKey() != null || item.getKey() != "") { |
| | | String key = item.getKey(); |
| | | Object val = item.getValue(); |
| | | if (!(val == "" || val == null)) { |
| | | sb.append(key + "=" + val + "&"); |
| | | } |
| | | } |
| | | } |
| | | sb.append("key=" + key); |
| | | String sign = MD5AndKL.MD5Encode(sb.toString(), "UTF-8").toUpperCase(); //注:MD5签名方式 |
| | | return sign; |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | | return null; |
| | | } |
| | | public static void main(String[] args) throws Exception { |
| | | SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS"); |
| | | String code = sdf.format(new Date()) + UUIDUtil.getNumberRandom(5); |
| | | String string = PaymentUtil.nativePay(code,new BigDecimal("0.1"), |
| | | "/test" |
| | | ); |
| | | System.err.println(string); |
| | | } |
| | | /** |
| | | * 查询支付订单 |
| | | * @param orderNo 订单号 |
| | |
| | | } |
| | | |
| | | |
| | | public static void main(String[] args) { |
| | | // UniPayResult uniPayResult = PaymentUtil.uniPay("852963742", 0.01D, "测试商品", "这是用于对接支付测试的商品描述", |
| | | // "", "/order/shopping-cart/shoppingCartPaymentCallback", "ooOrs64zHLuInkZ_GF0LpIN9_Rxc", "777168500885852"); |
| | | // PaymentUtil.queryOrder("852963742"); |
| | | // PaymentUtil.closeOrder("852963742"); |
| | | |
| | | } |
| | | } |