首先有两个东西,它们接口参数是不一样的
一个叫商户平台( https://pay.weixin.qq.com/),没有子商户
一个叫合作伙伴平台( https://pay.weixin.qq.com/index.php/partner/public/home),有主商户和子商户
一定要分清是哪一个,因为他们接口不一样。
我用的是商户平台,支付有很多封装好的工具包,直接用。

流程

[用户提交订单][服务端生成订单(out_trade_no)并调用统一下单接口(prepay_id)][服务端封装 prepay_id 生成调起支付参数返回给前端][前端调起微信支付(基于 prepay_id)][用户完成支付][微信回调通知服务器(支付成功)][服务器验签 + 记录支付状态]

JPay

引入依赖

<dependency>
    <groupId>com.github.javen205</groupId>
    <artifactId>IJPay-All</artifactId>
    <version>2.9.11</version>
</dependency>

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.70</version>
</dependency>

<dependency>
    <groupId>com.github.xkzhangsan</groupId>
    <artifactId>xk-time</artifactId>
    <version>3.0.1</version>
</dependency>

从证书中获取序列号

    /**
     * 从整数中获取证书序列号
     * @return
     * @throws Exception
     */
    public String getSerialNumber() throws Exception {
        X509Certificate certificate = PayKit.getCertificate("D:\\cert\\apiclient_cert.pem");
        String serialNumber = certificate.getSerialNumber().toString(16).toUpperCase(Locale.getDefault());
        return serialNumber;
    }

下单

import com.alibaba.fastjson2.JSONObject;
import com.ijpay.core.IJPayHttpResponse;
import com.ijpay.core.enums.RequestMethodEnum;
import com.ijpay.core.kit.PayKit;
import com.ijpay.core.utils.DateTimeZoneUtil;
import com.ijpay.wxpay.WxPayApi;
import com.ijpay.wxpay.enums.WxApiType;
import com.ijpay.wxpay.enums.WxDomainEnum;
import com.ijpay.wxpay.model.v3.Amount;
import com.ijpay.wxpay.model.v3.UnifiedOrderModel;

import java.security.cert.X509Certificate;
import java.util.Locale;

public class TestPay {


    public static void main(String[] args) throws Exception{

        UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel();
        //appid
        unifiedOrderModel.setAppid("");
        //mchid
        unifiedOrderModel.setMchid("");
        //描述
        unifiedOrderModel.setDescription("111111");
        //商户系统内部订单号,商户系统内部唯一,只能是数字、大小写字母_-|*@ ,同一退款号下不可重复使用。
        unifiedOrderModel.setOut_trade_no("12323213");
        //订单失效时间
        unifiedOrderModel.setTime_expire(DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 15));
        JSONObject attach = new JSONObject();
        attach.put("tradeNo", "1232132132");
        //附加数据,支付成功后查询订单API和支付成功回调通知均会将此字段返回给商户
        unifiedOrderModel.setAttach(JSONObject.toJSONString(attach));
        //支付成功的回调地址
        unifiedOrderModel.setNotify_url("https://www.baidu.com");
        Amount amount = new Amount();
        amount.setTotal(1);
        unifiedOrderModel.setAmount(amount);
        //订单金额,单位为分,整型
        String unifiedOrderData = JSONObject.toJSONString(unifiedOrderModel);
        //获取证书序列号
        X509Certificate certificate = PayKit.getCertificate("D:\\cert\\apiclient_cert.pem");
        String serialNumber = certificate.getSerialNumber().toString(16).toUpperCase(Locale.getDefault());
        //发起请求
        IJPayHttpResponse payResponse = WxPayApi.v3(
                RequestMethodEnum.POST,
                WxDomainEnum.CHINA.toString(),
                WxApiType.APP_PAY.toString(),
                appid,
                serialNumber,
                null,
                "D:\\cert\\apiclient_key.pem",
                unifiedOrderData
        );
        System.out.println(payResponse);
    }
}

IJPayHttpResponse{body='{"prepay_id":"wx251746560968658bf5a732a819c00a0001"}', status=200

根据out_trade_no查询订单

   /**
     * 商户订单号查询订单
     *      out_trade_no
     * @throws Exception
     */
    @Test
    public void test2() throws Exception {


        Map<String, String> params = new HashMap<>(16);
        params.put("mchid", "mchid");

        IJPayHttpResponse ijPayHttpResponse = WxPayApi.v3(
                RequestMethodEnum.GET,
                WxDomainEnum.CHINA.toString(),
                String.format(BasePayApiEnum.ORDER_QUERY_BY_OUT_TRADE_NO.toString(),12323213),
                "mchid",
                getSerialNumber(),
                null,
                "D:\\mchid_20240816_cert\\apiclient_key.pem",
                params
        );
        System.out.println(ijPayHttpResponse);
    }
IJPayHttpResponse{body='{"amount":{"payer_currency":"CNY","total":1},"appid":"","mchid":"","out_trade_no":"12323213","promotion_detail":[],"scene_info":{"device_id":""},"trade_state":"NOTPAY","trade_state_desc":"订单未支付"}', status=200

支付回调

@RequestMapping(value = "/payNotify", method = {RequestMethod.POST, RequestMethod.GET})
	@ResponseBody
	public void payNotify(HttpServletRequest request, HttpServletResponse response) {
		Map<String, String> map = new HashMap<>(12);
		try {
			String timestamp = request.getHeader("Wechatpay-Timestamp");
			String nonce = request.getHeader("Wechatpay-Nonce");
			String serialNo = request.getHeader("Wechatpay-Serial");
			String signature = request.getHeader("Wechatpay-Signature");

			log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature);
			String result = HttpKit.readData(request);
			log.info("支付通知密文 {}", result);
			String plainText = null;
			// 需要通过证书序列号查找对应的证书,verifyNotify 中有验证证书的序列号
//			String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp,
//				wxPayV3Bean.getApiKey3(), wxPayV3Bean.getPlatformCertPath());
			// 微信公钥验证签名并解密
			if (StringUtils.equals(serialNo, wxPayV3Bean.getPublicKeyId())) {
				plainText = WxPayKit.verifyPublicKeyNotify(result, signature, nonce, timestamp,
					wxPayV3Bean.getApiKey3(), wxPayV3Bean.getPlatformCertPath());
			}
			log.info("支付通知明文 {}", plainText);

			if (StrUtil.isNotEmpty(plainText)) {
				response.setStatus(200);
				map.put("code", "SUCCESS");
				map.put("message", "SUCCESS");
			} else {
				response.setStatus(500);
				map.put("code", "ERROR");
				map.put("message", "签名错误");
			}
			response.setHeader("Content-type", ContentType.JSON.toString());
			response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8));
			response.flushBuffer();
		} catch (Exception e) {
			log.error("系统异常", e);
		}
	}

协议错误

SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate)
	at com.ijpay.core.http.AbstractHttpDelegate.post(AbstractHttpDelegate.java:297)
	at com.ijpay.core.http.AbstractHttpDelegate.post(AbstractHttpDelegate.java:312)

在这里插入图片描述

其它

商户订单号重复

每次发送请求唤起支付,out_trade_no只能唯一。除非金额和其它项没有变,就可以继续使用同一个out_trade_no进行支付请求。

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐