微信支付分(一)–创建支付分订单


1、介绍

代码地址: https://github.com/xm646236438/wechat_pay_score/tree/master
使用SpringBoot做的

第一眼看到文档, 确实有些懵逼
加上提供的文档, 可能是我懒, 真的不想看(不过说实话, 现在回头看看提供的文档, 还是很OK的)
废话不多说, 官方文档走起
官方文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore/chapter2_1.shtml

下一篇博客: 微信支付分(二)–查询支付分订单

2、参数介绍以及对应的位置

appid : 公众账号ID, 小程序, 公众号的都可以
在这里插入图片描述



service_id : 在申请后就会给到你, 如果没有就找申请的人要



serial_no: 证书序列号
在这里插入图片描述




商户号:
在这里插入图片描述



秘钥 证书,

重点, 如果秘钥已经设置了, 就问别人要, 如果有线上的在使用, 你懂的


在这里插入图片描述

3、贴代码, 具体代码github

创建支付分的签名:

package com.tomorrow.wechat_pay_score.util.wechart;

import com.tomorrow.wechat_pay_score.util.Utils;
import lombok.extern.slf4j.Slf4j;
import okhttp3.HttpUrl;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.io.ClassPathResource;

import java.io.IOException;
import java.io.InputStream;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Scanner;

/**
 * @author Tomorrow
 * @date 2020/5/23 1:00
 */
@Slf4j
public class PayScore {
    
    

    /**
     * 微信支付API v3 签名
     *
     * @param method       请求类型GET、POST
     * @param url          请求地址
     * @param body         请求数据 GET: 传"" POST: json串
     * @param merchantId   商户号
     * @param certSerialNo 证书序列号
     * @param filename     API证书相对路径
     * @return
     * @throws Exception
     */
    public static String getToken(String method, String url, String body, String merchantId, String certSerialNo, String filename) throws Exception {
    
    
        String signStr = "";
        HttpUrl httpurl = HttpUrl.parse(url);
        // 随机字符串
        String nonceStr = Utils.getRandomString(32);
        // 时间戳
        long timestamp = System.currentTimeMillis() / 1000;
        if (StringUtils.isEmpty(body)) {
    
    
            body = "";
        }
        String message = buildMessage(method, httpurl, timestamp, nonceStr, body);
        String signature = sign(message.getBytes("utf-8"), filename);
        signStr = "mchid=\"" + merchantId
                + "\",nonce_str=\"" + nonceStr
                + "\",timestamp=\"" + timestamp
                + "\",serial_no=\"" + certSerialNo
                + "\",signature=\"" + signature + "\"";
        log.info("Authorization Token:" + signStr);
        return signStr;
    }

    public static String buildMessage(String method, HttpUrl url, long timestamp, String nonceStr, String body) {
    
    
        String canonicalUrl = url.encodedPath();
        if (url.encodedQuery() != null) {
    
    
            canonicalUrl += "?" + url.encodedQuery();
        }
        return method + "\n"
                + canonicalUrl + "\n"
                + timestamp + "\n"
                + nonceStr + "\n"
                + body + "\n";
    }


    public static String sign(byte[] message, String filename) throws Exception {
    
    
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(getPrivateKey(filename));
        sign.update(message);
        return Base64.encodeBase64String(sign.sign());
    }

    /**
     * 获取私钥。
     *
     * @return 私钥对象
     */
    public static PrivateKey getPrivateKey(String filename) throws IOException {
    
    
        // 编译后的相对路径
        ClassPathResource classPathResource = new ClassPathResource(filename);
        InputStream inputStream = classPathResource.getInputStream();
        Scanner scanner = new Scanner(inputStream, "UTF-8");
        String content = scanner.useDelimiter("\\A").next();
        // 绝对路径
//        String content = new String(Files.readAllBytes(Paths.get("F:\\key\\publicKey.pem")), "utf-8");
        try {
    
    
            String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
                    .replace("-----END PRIVATE KEY-----", "")
                    .replaceAll("\\s+", "");
            KeyFactory kf = KeyFactory.getInstance("RSA");
            return kf.generatePrivate(
                    new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey)));
        } catch (NoSuchAlgorithmException e) {
    
    
            throw new RuntimeException("当前Java环境不支持RSA", e);
        } catch (InvalidKeySpecException e) {
    
    
            log.info("异常:" + e);
            throw new RuntimeException("无效的密钥格式");
        }
    }
}

业务逻辑:

@Override
    public CommonResult wakeUpPaymentPoints(String orderNo, int depositAmount) {
    
    
        // 创建支付分订单 请求参数
        JSONObject parameters = new JSONObject();
        parameters.put("out_order_no", orderNo);
        parameters.put("appid", appId);
        parameters.put("service_id", serviceId);
        parameters.put("service_introduction", "阿啵呲嘚");
        JSONObject timeRange = new JSONObject();
        timeRange.put("start_time", "OnAccept");
        parameters.put("time_range", timeRange);
        JSONObject riskFund = new JSONObject();
        riskFund.put("name", "DEPOSIT");
        riskFund.put("amount", depositAmount);
        riskFund.put("description", "阿啵呲嘚");
        parameters.put("risk_fund", riskFund);
        parameters.put("notify_url", notifyURL);
        parameters.put("need_user_confirm", true);

        JSONObject jsonObject;
        try {
    
    
            log.info("请求参数", JSONObject.toJSONString(parameters));
            String data = HttpRequest.post(createOrderUrl)
                    .header(Header.CONTENT_TYPE, "application/json")
                    .header(Header.ACCEPT, "application/json")
                    // 签名
                    .header("Authorization", "WECHATPAY2-SHA256-RSA2048" + " "
                            + PayScore.getToken("POST", createOrderUrl, JSONObject.toJSONString(parameters), mchId, serialNo, "pem/apiclient_key.pem"))
                    .body(JSONObject.toJSONString(parameters))
                    .execute().body();
            jsonObject = JSONObject.parseObject(data);
            System.out.println("返回参数" + jsonObject);
        }catch (Exception e) {
    
    
            throw new SpringExceptionResolver("500", "网络超时!");
        }
        if (!"CREATED".equals(jsonObject.getString("state"))) {
    
    
            throw new SpringExceptionResolver("500", jsonObject.getString("message"));
        }

        // 处理返回数据, 前端拿到后可以进行直接使用
        SortedMap<Object, Object> result = new TreeMap<Object, Object>();
        result.put("mch_id", mchId);
        result.put("package", jsonObject.getString("package"));
        result.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
        result.put("nonce_str", Utils.getRandomString(32));
        result.put("sign_type", "HMAC-SHA256");
        // 签名
        result.put("sign", HMACSHA256.sha256_HMAC(result, mchKey));
        return CommonResult.success("SUCCESS", result);
    }

HMAC-SHA256签名: 校验地址

package com.tomorrow.wechat_pay_score.util.wechart;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.Map;
import java.util.SortedMap;

/**
 * @author Tomorrow
 * @date 2020/5/23 1:00
 */
public class HMACSHA256 {
    
    
    /**
     * 将加密后的字节数组转换成字符串
     *
     * @param b 字节数组
     * @return 字符串
     */
    public static String byteArrayToHexString(byte[] b) {
    
    
        StringBuilder hs = new StringBuilder();
        String stmp;
        for (int n = 0; b != null && n < b.length; n++) {
    
    
            stmp = Integer.toHexString(b[n] & 0XFF);
            if (stmp.length() == 1)
                hs.append('0');
            hs.append(stmp);
        }
        return hs.toString().toLowerCase();
    }

    /**
     * sha256_HMAC加密
     *
     * @param parameters 参数
     * @param key        秘钥
     * @return 加密后字符串
     */
    public static String sha256_HMAC(SortedMap<Object, Object> parameters, String key) {
    
    
        // 对数据进行排序
        StringBuilder sb = new StringBuilder();
        for (Map.Entry entry : parameters.entrySet()) {
    
    
            // 去除掉空参数以及sign
            if (entry.getValue() != null && entry.getKey() != "sign") {
    
    
                sb = sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
            }
            ;
        }
        // 拼接API密钥
        sb.append("key=" + key);
        // 待签名字符串
        String message = sb.toString();

        String hash = "";
        try {
    
    
            Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
            SecretKeySpec secret_key = new SecretKeySpec(key.getBytes(), "HmacSHA256");
            sha256_HMAC.init(secret_key);
            byte[] bytes = sha256_HMAC.doFinal(message.getBytes());
            hash = byteArrayToHexString(bytes).toUpperCase();
        } catch (Exception e) {
    
    
            System.out.println("Error HmacSHA256 ===========" + e.getMessage());
        }
        return hash;
    }
}

4、测试结果

在这里插入图片描述

{
    
    
    "code": 200,
    "message": "SUCCESS",
    "data": {
    
    
        "mch_id": "小嘛小儿郎背着那书包上学堂",
        "nonce_str": "4i8tNute22VdlN5UXpu8a1UwT5h11813",
        "package": "小嘛小儿郎背着那书包上学堂",
        "sign": "868D29B5867A51F6B53C9180B54B9BD527638EDF0F30A2A3D5EDEA95B2971B02",
        "sign_type": "HMAC-SHA256",
        "timestamp": "1591426290"
    }
}

比如小程序就可以直接用啦:
https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore/chapter8_2.shtml
在这里插入图片描述




5、杂谈

1、这个做完, 就相当于已经完成三分之一了
2、还有一个很重要的事情,感谢 蛋挞小子:https://www.cnblogs.com/nginxTest/p/12697136.html 签名借鉴了一波, 哈哈哈, 感谢感谢
3、GitHub上面的提交的代码, 只是一个列子, 要视自己的具体业务逻辑进行修改一下就行
4、其实很简单, 很简单, 很简单
5、时间太晚了, 写的就粗糙了一些, 包含包含
6、代码地址: https://github.com/xm646236438/wechat_pay_score/tree/master
7、关于证书,因为证书会更新以及过期, 这个地方最好是动态获取, 而不是从商户号拿,这个地方后期补充文章
8、碰到的问题可以在此处搜索:https://developers.weixin.qq.com/community/pay/doc/0004060fa7c65855d698166145b808?blockType=8
9、系统繁忙. 和微信技术沟通过, 说是系统有时会这样, 属于正常的.会有技术对接群, 可以询问在这里插入图片描述






提供一个群:807770565,欢迎各位进来尬聊

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_38637558/article/details/106294154