微信支付V3(公众号支付demo)

之前没排版,看起来很难受,现在舒服多了!

准备工作

商户证书

微信支付证书

通知地址notifyUrl(必须外网可访问!)

1、组装必要参数,调用下单接口

// 自动更新微信支付证书

private static AutoUpdateCertificatesVerifier verifier;

// 商户私钥(privateKey:一串特别长的base64串)
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream(privateKey.getBytes("utf-8")));

// 使用自动更新的签名验证器,不需要传入证书
verifier = new AutoUpdateCertificatesVerifier(
      new WechatPay2Credentials(mchid, new PrivateKeySigner(mchSerialNo, merchantPrivateKey)),
      apiV3Key.getBytes("utf-8"));

// 构建httpClient
httpClient = WechatPayHttpClientBuilder.create().withMerchant(mchid, mchSerialNo, merchantPrivateKey)
      .withValidator(new WechatPay2Validator(verifier)).build();

// v3下单接口
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi");

// 设置订单失效时间4分钟(微信要求日期格式)
long expireTime = System.currentTimeMillis()+4*60*1000;
DateFormat sf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
Date date = new Date(expireTime);
String format = sf.format(date);

// 组装订单json串
String data = "{\r\n" +
    "    \"time_expire\": \""+ format +"\",\r\n" +
    "    \"amount\": {\r\n" +
    "        \"total\": "+ amount +",\r\n" +
    "        \"currency\": \"CNY\"\r\n" +
    "    },\r\n" +
    "    \"mchid\": \""+ mchid +"\",\r\n" +
    "    \"description\": \""+ orgiValue +",数量:" + numI + "客服微信:qzy345371236"+"\",\r\n" +
    "    \"notify_url\": \""+ WeChatPayConstants.notifyUrl +"\",\r\n" +
    "    \"payer\": {\r\n" +
    "        \"openid\": \""+ openid +"\"\r\n" +
    "    },\r\n" +
    "    \"out_trade_no\": \""+ outTradeNo +"\",\r\n" +
    "    \"appid\": \""+ appid +"\",\r\n" +
    "    \"attach\": \""+ orgi +"\",\r\n" +
    "    \"scene_info\": {\r\n" +
    "        \"payer_client_ip\": \""+ createIp +"\"\r\n" +
    "    }\r\n" +
    "}";

// 订单json串
StringEntity reqEntity = new StringEntity(data, ContentType.create("application/json", "utf-8"));
httpPost.setEntity(reqEntity);
httpPost.addHeader("Accept", "application/json");
// 微信支付证书序列号
httpPost.addHeader("Wechatpay-Serial", wechatPaySerial);

// 执行下单
CloseableHttpResponse response1 = httpClient.execute(httpPost);
try {
    HttpEntity entity = response1.getEntity();
    String s = EntityUtils.toString(entity);
    JSONObject parseObject = JSONObject.parseObject(s);
    String prepayId = parseObject.getString("prepay_id");
    EntityUtils.consume(entity);
    long now = System.currentTimeMillis()/1000;
    String nonceStr = SignUtil.generateNonceStr();
    prepayId = "prepay_id="+prepayId;
    String token = SignUtil.getToken(appid, prepayId, now, nonceStr);
    // 组装唤起微信支付
    mmap.put("appId", appid);
    mmap.put("timeStamp", now);
    mmap.put("nonceStr", nonceStr);
    mmap.put("packageStr", prepayId);
    mmap.put("paySign", token);
    mmap.put("outTradeNo", outTradeNo);
    if (!StringUtils.isBlank(returnUrl)) {
        mmap.put("returnUrl", returnUrl);// 付款成功后返回的地址
    }
}
finally {
    response1.close();
}
return "pay";// 唤起微信支付的页面路径

2、唤起支付页面直接用微信给的demo,改改路径参数直接用就行!

在这里插入图片描述

3、支付回调

boolean successFlag = true;
HttpServletRequest request;
// 获取回调报文
request.setCharacterEncoding("UTF-8");
int size = request.getContentLength();
InputStream is = request.getInputStream();
byte[] reqBodyBytes = readBytes(is, size);
String resBody = new String(reqBodyBytes);

//回调响应头必要数据,没有这些参数说明回调不对
String headerTimestamp = request.getHeader("Wechatpay-Timestamp");
String headerNonce = request.getHeader("Wechatpay-Nonce");
String headerSerial = request.getHeader("Wechatpay-Serial");
String headerSignature = request.getHeader("Wechatpay-Signature");
if(headerTimestamp==null || headerNonce==null || headerSerial==null || headerSignature==null) {
    logger.error("回调响应头参数有误!");
    failReason = "回调响应头参数有误!";
    successFlag = false;
}else {
	// 组装验证回调微信支付签名必要参数
	PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream(privateKey.getBytes("utf-8")));
	verifier = new AutoUpdateCertificatesVerifier(
	        new WechatPay2Credentials(mchid, new PrivateKeySigner(mchSerialNo, merchantPrivateKey)),
	        apiV3Key.getBytes("utf-8"));
	String message = headerTimestamp + "\n" + headerNonce + "\n" + resBody + "\n";
	// 执行验证回调微信支付签名
	boolean verify = verifier.verify(headerSerial, message.getBytes(StandardCharsets.UTF_8), headerSignature);
	if(!verify) {
	    logger.error("回调验证签名失败!");
	    failReason = "回调验证签名失败!";
	    successFlag = false;
	}
	//验证头部携带的序列号是否和付款时候一致,不一致说明有可能被串改,不执行后续操作
	if(!wechatPaySerial.equals(headerSerial)) {
	    logger.error("回调的微信支付证书序列号与请求的不一致!");
	    failReason = "回调的微信支付证书序列号与请求的不一致!";
	    successFlag = false;
	}
}

if(successFlag) {
   	//解析回调主体
    JSONObject po = JSON.parseObject(resBody);
    JSONObject jo = po.getJSONObject("resource");
    String ciphertext = jo.getString("ciphertext");
    String nonce = jo.getString("nonce");
    String associatedData = jo.getString("associated_data");
    //解密密文
    String jsonData = "";
    AesUtil decryptor = new AesUtil(apiV3Key.getBytes(StandardCharsets.UTF_8));
    if(associatedData != null) {
        jsonData = decryptor.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8), nonce.getBytes(StandardCharsets.UTF_8), ciphertext);
    }else {
        jsonData = decryptor.decryptToString(null, nonce.getBytes(StandardCharsets.UTF_8), ciphertext);
    }
    //解密完之后的资源数据
    HashMap<String, Object> map = JSON.parseObject(jsonData, HashMap.class);
    //商户订单号
    String outTradeNo = (String) map.get("out_trade_no");
    String tradeState = (String) map.get("trade_state");
    
    if ("SUCCESS".equals(tradeState)) {
        logger.info("success,测试回调验签成功");
        // 1.查询订单,修改订单状态
    }
}
// 返回响应
response.setContentType("text/html;charset=UTF-8");
response.setCharacterEncoding("UTF-8");
response.getOutputStream().write(响应code.getBytes());
response.flushBuffer();

//回调结束

// readBytes工具
public static final byte[] readBytes(InputStream is, int contentLen) {
    if (contentLen > 0) {
        int readLen = 0;
        int readLengthThisTime = 0;
        byte[] message = new byte[contentLen];
        try {
            while (readLen != contentLen) {
                readLengthThisTime = is.read(message, readLen, contentLen - readLen);
                if (readLengthThisTime == -1) {// Should not happen.
                    break;
                }
                readLen += readLengthThisTime;
            }
            return message;
        } catch (IOException e) {
        }
    }
    return new byte[] {};
}

注意:商户证书和微信支付证书一定要分清楚!

微信支付 PHP V3 JSAPI Demo是一个用于演示使用PHP语言开发的微信支付服务的案例。 在这个Demo中,我们将展示如何通过JSAPI(公众号支付)来实现用户在微信公众号内进行支付的功能。 首先,我们需要准备好开发环境。确保我们已经安装了PHP、服务器环境以及微信支付SDK。接着,我们可以创建一个基本的支付页面。 在这个页面中,我们需要引入微信支付提供的JSAPI SDK,以及自己的JS代码文件。然后,我们需要编写相应的PHP代码来处理用户提交的支付请求并生成支付订单。 在用户点击支付按钮后,我们可以通过微信JSAPI SDK提供的接口来获取用户的openid(用户在微信公众号内的唯一标识)。我们可以使用这个openid来标识用户的身份。 接下来,我们需要生成一个唯一的商户订单号,并将订单信息发送给微信支付服务器进行支付处理。在生成订单时,我们需要包括商品的价格、商品描述以及支付回调地址等信息。这些信息在生成订单时都需要进行签名,以确保安全性。 生成订单后,我们需要将订单信息以JSON格式返回给前端页面。前端页面可以通过调用微信JSAPI SDK提供的接口来发起支付请求。支付请求会打开微信支付页面供用户输入密码进行支付。 一旦用户完成支付微信支付服务器会向我们提供的回调地址发送一个支付结果通知。我们需要在回调函数中处理这个通知,并校验通知的合法性。如果通知合法,我们可以更新订单的支付状态,并进行相关的业务处理。 以上就是微信支付 PHP V3 JSAPI Demo的基本流程。通过这个案例,我们可以了解如何使用PHP语言来实现微信公众号支付功能,并与微信支付服务器进行交互。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值