WeChat Pay Sub (1)-Create Payment Sub Order


1 Introduction

Code address: https://github.com/xm646236438/wechat_pay_score/tree/master made
using SpringBoot

At first glance, I saw the documentation, it was indeed a bit daunting. With the
provided documentation, I may be lazy and I really don’t want to read it (but to be honest, now I look back at the provided documentation, it’s still very ok)
Not much nonsense, official The document starts from the
official document: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore/chapter2_1.shtml

Next blog: WeChat payment points (2)-query payment points order

2. Parameter introduction and corresponding location

appid: official account ID, mini program, official account can be
Insert picture description here



service_id: will be given to you after the application, if not, ask the applicant for



serial_no: certificate serial number
Insert picture description here




Merchant number:
Insert picture description here



secret key certificate,

The point is, if the secret key has been set, just ask others for it, if the wired one is in use, you know


Insert picture description here

3. Post code, specific code github

Create a signature for payment points:

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("无效的密钥格式");
        }
    }
}

Business logic:

@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 signature: verify address

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. Test results

Insert picture description here

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

For example, the mini program can be used directly:
https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore/chapter8_2.shtml
Insert picture description here




5. Miscellaneous talk

1, this done, has completed the equivalent of one-third of the
2, there is a very important thing, thank tarts kid : https: //www.cnblogs.com/nginxTest/p/12697136.html signature draws A wave, hahaha, thank you
3. The submitted code on GitHub is just an example, you have to modify it according to your specific business logic
4. It is actually very simple, very simple, very simple
5. It is too late, The writing is a bit rougher, including
6. The code address: https://github.com/xm646236438/wechat_pay_score/tree/master
7. About the certificate, because the certificate will be updated and expired, this place is best to be obtained dynamically, and It is not taken from the merchant
account . This place is supplemented in the later article 8. The problems encountered can be searched here: https://developers.weixin.qq.com/community/pay/doc/0004060fa7c65855d698166145b808?blockType=8
9. The system is busy . Communicate with WeChat technology, saying that the system sometimes does this, which is normal. There will be a technology docking group, you can askInsert picture description here






Provide a group: 807770565, welcome everyone to come in and chat

Insert picture description here

Guess you like

Origin blog.csdn.net/qq_38637558/article/details/106294154