微信小程序支付java端apiv3版

背景 :网上支付这块资料比较多也比较杂,有些是基于老版本接口的文章,虽然说明很详细写的很好阅读量很大, 但是不是那么适用。现在最新版本apiv3,基于此基础上做的支付开发。

官方文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_8_2.shtml

业务流程图如下:多看几遍,脑袋里得知道整体流转过程。
在这里插入图片描述
1、2两步,服务端业务逻辑,主要目的是服务端生成自己的订单记录和相关数据,后面支付使用。

3、4、5步 小程序下单,将1、2步服务端订单相关参数,按照接口要求格式组装好,请求微信支付系统,生成预支付交易订单;
说明:
(1)、毕竟是涉及交易请求,httpclient比平常需要做些安全初始化操作,创建加载商户私钥、加载平台证书、初始化httpClient的通用方法,官方说明2.1.搭建和配置开发环境有对应说明。
(2)、具体参数说明见官方说明

public JSONObject createOrder(BigDecimal total, String orderNo) throws Exception{
    SUser sysUser = (SUser) SecurityUtils.getSubject().getPrincipal();
    String openid=sysUser.getOpenid();

    File directory = new File("..");// 参数为空
    String courseFile = directory.getCanonicalPath()+"/apiclient_key.pem";
    PrivateKey privateKey=getPrivateKey(courseFile);

    JSONObject map = new JSONObject();

    // 加载平台证书(mchId:商户号,mchSerialNo:商户证书序列号,apiV3Key:V3秘钥)
    AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
            new WechatPay2Credentials(mchId, new PrivateKeySigner(mchSerialNo, privateKey)),apiV3Key.getBytes("utf-8"));

    // 初始化httpClient
    // *********************** 此处有坑 HttpClient 与文档有出入
    HttpClient httpClient = WechatPayHttpClientBuilder.create()
            .withMerchant(mchId, mchSerialNo, privateKey)
            .withValidator(new WechatPay2Validator(verifier)).build();

    //请求URL
    HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi");
    //********************参数去参考文档,自定义
    //随机字符串  根据自己工具类写
    String nonceStr = RandomStringUtils.randomAlphanumeric(5);
    //时间戳 根据自己工具类写
    String timeStamp = String.valueOf(System.currentTimeMillis());

    //微信金额是按照分来的,所以要乘以100
    total=total.multiply(new BigDecimal(100));

    // 请求body参数
    String reqdata = "{"
            + "\"amount\": {"
            + "\"total\": " + total + ","
            + "\"currency\": \"CNY\""
            + "},"
            + "\"mchid\": \""+mchId+ "\","
            + "\"attach\": \""+orderNo + "\","
            + "\"description\": \"" + orderNo+ "\","
            + "\"notify_url\": \""+notifyUrl+"\","
            + "\"payer\": {"
            + "\"openid\": \"" + openid  +"\"" + "},"
            + "\"out_trade_no\": \"" +  orderNo + "\","
            + "\"goods_tag\": \"WXG\","
            + "\"appid\": \""+ appId+"\"" + "}";
    StringEntity entity = new StringEntity(reqdata);
    entity.setContentType("application/json");
    httpPost.setEntity(entity);
    httpPost.setHeader("Accept", "application/json");

    //完成签名并执行请求
    String result = "";
    CloseableHttpResponse response = (CloseableHttpResponse) httpClient.execute(httpPost);
    try {
        int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode == 200) {
            System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
            JSONObject jsonObject = JSONObject.parseObject(EntityUtils.toString(response.getEntity()));
            result = "prepay_id=" + jsonObject.getString("prepay_id");;
        } else if (statusCode == 204) {
            System.out.println("success");
            result = "success";
        } else {
            result = "failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity());
            System.out.println(result);
            throw new IOException("request failed");
        }
    } finally {
        response.close();
    }

    //************************签名,自己拼接,然后进行两次加密,为什么加密,下面解释
    String pendingPaySign = appId + "\n" + timeStamp + "\n" + nonceStr + "\n" + result + "\n";
    String paySign = SignUtil.encodeBase64(SignUtil.sign256(pendingPaySign, privateKey));
    map.put("appId", appId);
    map.put("timeStamp", timeStamp);
    map.put("nonceStr", nonceStr);
    map.put("package", result);
    map.put("signType", "RSA");
    map.put("paySign", paySign);
    return map;
}

到此为止,预支付成功,获得了prepay_id,获得了prepay_id是为了第6步:生成带签名支付信息。
因为第7步,小程序前端需要第6步生成的带签名支付信息,发起支付请求。

7-14步是微信前端发起支付、用户确认支付的整个过程,后端不需要做什么
15-18步用户支付成功了,后台需要被通知到(异步的,告诉微信支付接口被通知回调的接口,支付成功后平台会按照这个接口地址调用,回调接口地址就是生成预支付订单参数:notify_url,关于服务端接收支付结果通知详细说明,参考官方说明3.2.3),然后才好做后续操作,例如:记录成功交易记录、发货等后续操作。详细说明地址:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_5.shtml
接收支付通知处理逻辑代码:

@Override
    public String notifyResult(HttpServletRequest request, HttpServletResponse response) throws Exception {
        JSONObject map = new JSONObject();
        String result = readData(request);
        // 需要通过证书序列号查找对应的证书,verifyNotify 中有验证证书的序列号
        String plainText = verifyNotify(result, apiV3Key);
        if (StringUtils.isNotEmpty(plainText)) {
            response.setStatus(200);
            map.put("code", "SUCCESS");
            map.put("message", "SUCCESS");

            JSONObject jsonObject=JSONObject.parseObject(plainText);
            String out_trade_no = jsonObject.getString("out_trade_no");
            String transaction_id = jsonObject.getString("transaction_id");
            String trade_state = jsonObject.getString("trade_state");
            String trade_state_desc = jsonObject.getString("trade_state_desc");
            String attach = jsonObject.getString("attach");
            String success_time = jsonObject.getString("success_time");
            String amount = jsonObject.getString("amount");
            String openid=jsonObject.getJSONObject("payer").getString("openid");
            updateOrder(attach,trade_state,openid);
        } else {
            response.setStatus(500);
            map.put("code", "ERROR");
            map.put("message", "签名错误");
        }
        //通知应答
        //支付通知http应答码为200或204才会当作正常接收,当回调处理异常时,应答的HTTP状态码应为500,或者4xx
        response.setCharacterEncoding("utf-8");
        response.setContentType("application/json; charset=utf-8");
        BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
        out.write(plainText.getBytes());
        out.flush();
        out.close();
        return null;
    }

19-21步是主动查询支付结果。
暂没考虑主动查询支付结果。

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是微信小程序支付ApiV3java开发的完整案例: 1. 首先,你需要在微信商户平台上创建一个应用并获取到以下信息: - 商户号(mch_id) - 商户APIv3密钥(api_key) - 应用ID(appid) - 商户证书私钥文件(apiclient_key.pem) - 商户证书文件(apiclient_cert.pem) - 微信支付平台证书文件(weixinpay_cert.pem) 2. 接下来,你需要使用Java SDK进行开发,包括以下步骤: - 导入相关的Java SDK依赖包。 - 构建请求参数,包括HTTP请求头和请求体,其中请求体需要使用商户证书进行签名。 - 发送HTTP请求,并处理响应数据。 以下是一个简单的Java代码片段,演示如何使用Java SDK实现微信小程序支付ApiV3本: ``` import com.github.wxpay.sdk.WXPay; import com.github.wxpay.sdk.WXPayConstants; import com.github.wxpay.sdk.WXPayUtil; import org.apache.commons.codec.binary.Base64; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.http.HttpEntity; import org.apache.http.HttpHeaders; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.security.*; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.HashMap; import java.util.Map; public class WxPayApiV3Demo { private static final String API_KEY = "商户APIv3密钥"; private static final String MCH_ID = "商户号"; private static final String APP_ID = "应用ID"; private static final String API_BASE_URL = "https://api.mch.weixin.qq.com/"; private static final String PRIVATE_KEY_FILE = "apiclient_key.pem"; private static final String CERT_FILE = "apiclient_cert.pem"; private static final String WEIXINPAY_CERT_FILE = "weixinpay_cert.pem"; private static final String CHARSET = StandardCharsets.UTF_8.name(); private static final String API_PAY = "v3/pay/transactions/jsapi"; private static final String API_CERTIFICATE = "v3/certificates"; private static PrivateKey privateKey; private static X509Certificate certificate; private static X509Certificate weixinpayCertificate; static { try { privateKey = loadPrivateKey(new File(PRIVATE_KEY_FILE)); certificate = loadCertificate(new File(CERT_FILE)); weixinpayCertificate = loadCertificate(new File(WEIXINPAY_CERT_FILE)); } catch (Exception e) { throw new RuntimeException("Failed to load certificate.", e); } } public static void main(String[] args) throws Exception { // 构建请求参数 Map<String, String> params = new HashMap<>(); params.put("appid", APP_ID); params.put("mchid", MCH_ID); params.put("description", "测试商品"); params.put("out_trade_no", "1234567890"); params.put("amount", "{\"total\":100,\"currency\":\"CNY\"}"); params.put("payer", "{\"openid\":\"用户openid\"}"); params.put("notify_url", "https://example.com/notify"); // 发送HTTP请求 String response = doPost(API_PAY, params); System.out.println(response); } private static String doPost(String api, Map<String, String> params) throws Exception { String url = API_BASE_URL + api; String timestamp = String.valueOf(System.currentTimeMillis() / 1000); // 构建请求头 Map<String, String> headers = new HashMap<>(); headers.put(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.getMimeType()); headers.put(HttpHeaders.AUTHORIZATION, buildAuthorizationHeader(api, timestamp, params)); // 构建请求体 String requestBody = WXPayUtil.mapToXml(params); String signature = sign(requestBody, timestamp); String signedRequestBody = buildSignedRequestBody(requestBody, signature); // 发送HTTP请求 HttpPost httpPost = new HttpPost(url); headers.forEach(httpPost::addHeader); httpPost.setEntity(new StringEntity(signedRequestBody, CHARSET)); try (CloseableHttpClient httpClient = HttpClients.createDefault(); CloseableHttpResponse httpResponse = httpClient.execute(httpPost)) { HttpEntity entity = httpResponse.getEntity(); return IOUtils.toString(entity.getContent(), CHARSET); } } private static String buildAuthorizationHeader(String api, String timestamp, Map<String, String> params) throws Exception { String nonceStr = WXPayUtil.generateUUID(); String message = buildMessage(api, timestamp, nonceStr, params); String signature = sign(message); return String.format("WECHATPAY2-SHA256-RSA2048 mchid=\"%s\",nonce_str=\"%s\",timestamp=\"%s\",serial_no=\"%s\",signature=\"%s\"", MCH_ID, nonceStr, timestamp, weixinpayCertificate.getSerialNumber().toString(16), signature); } private static String buildMessage(String api, String timestamp, String nonceStr, Map<String, String> params) { StringBuilder sb = new StringBuilder(); sb.append("POST\n") .append(API_BASE_URL).append(api).append("\n") .append(buildQueryString(params)).append("\n") .append(HttpHeaders.CONTENT_TYPE).append(":").append(ContentType.APPLICATION_JSON.getMimeType()).append("\n") .append(HttpHeaders.ACCEPT).append(":").append(ContentType.APPLICATION_JSON.getMimeType()).append("\n") .append(HttpHeaders.AUTHORIZATION).append(":").append(buildAuthorizationHeader(api, timestamp, params)).append("\n") .append("Wechatpay-Timestamp:").append(timestamp).append("\n") .append("Wechatpay-Nonce:").append(nonceStr).append("\n") .append("Wechatpay-Serial:").append(weixinpayCertificate.getSerialNumber().toString(16)).append("\n"); return sb.toString(); } private static String buildQueryString(Map<String, String> params) { StringBuilder sb = new StringBuilder(); params.entrySet().stream() .sorted(Map.Entry.comparingByKey()) .forEach(entry -> { if (sb.length() > 0) { sb.append("&"); } sb.append(entry.getKey()).append("=").append(entry.getValue()); }); return sb.toString(); } private static String sign(String message) throws Exception { Signature signature = Signature.getInstance("SHA256withRSA"); signature.initSign(privateKey); signature.update(message.getBytes(StandardCharsets.UTF_8)); return Base64.encodeBase64String(signature.sign()); } private static String sign(String requestBody, String timestamp) throws Exception { StringBuilder sb = new StringBuilder(); sb.append(timestamp).append("\n") .append(nonceStr()).append("\n") .append(requestBody).append("\n"); return sign(sb.toString()); } private static String buildSignedRequestBody(String requestBody, String signature) { return requestBody.replace("</xml>", String.format("<sign_type><![CDATA[SHA256]]></sign_type><sign><![CDATA[%s]]></sign></xml>", signature)); } private static String nonceStr() { return WXPayUtil.generateUUID(); } private static PrivateKey loadPrivateKey(File file) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException { byte[] bytes = FileUtils.readFileToByteArray(file); PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes); KeyFactory factory = KeyFactory.getInstance("RSA"); return factory.generatePrivate(spec); } private static X509Certificate loadCertificate(File file) throws IOException, CertificateException { byte[] bytes = FileUtils.readFileToByteArray(file); return WXPayUtil.loadCertificate(bytes); } } ``` 上述代码中,我们使用了微信提供的Java SDK(https://github.com/wechatpay-apiv3/wechatpay-apache-httpclient),它封装了HTTP请求和签名验证的相关逻辑,并提供了一些实用的工具类。在这个示例中,我们使用了WXPayUtil工具类来进行签名、验签和XML格式的转换。我们还使用了Apache HttpClient来发送HTTP请求。 需要注意的是,该示例中使用的是微信提供的Java SDK中的最新本,如果你使用的是旧本,可能会存在一些差异。 此外,该示例中只演示了如何实现支付接口,如果你需要实现其他接口,可以参考微信官方文档(https://wechatpay-api.gitbook.io/wechatpay-api-v3/)。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值