WeChat ist direkt mit der Händler-V3-Zahlung verbunden (kann direkt verwendet werden)

Der Unterschied zwischen dem direkt verbundenen Händlermodell und dem Service-Provider-Modell:

        Direkt mit Händlern verbunden: Beispielsweise hat Zhang San ein kleines Programm eröffnet, und dann kauft jemand etwas in diesem kleinen Programm. Beim Auschecken wird das Geld direkt auf das Konto von Zhang San überwiesen.

        Dienstleistermodell: Zhang San hat beispielsweise ein kleines Programm eröffnet, und dann hat dieses kleine Programm die Funktion, eine Filiale zu eröffnen, und wenn dann jemand in der Filiale etwas kauft, wird das Geld direkt auf das Konto der verantwortlichen Person überwiesen der Filiale beim Auschecken
        In diesem Artikel geht es um das direkt verbundene Händlermodell (siehe Service-Provider-Modell: https://blog.csdn.net/qq_26112725/article/details/131684591 )

WeChat-Zahlungslogik (Schwerpunkt):

44ce6b79061943038deae8091cf7e087.png


        Das Front-End klickt auf die Zahlungsschaltfläche und bevor die WeChat-Zahlungsseite gestartet wird, wird eine Anfrage an das Back-End gesendet. Das Back-End ist für den Aufruf der Schnittstelle „Einheitliche Bestellung“ von WeChat verantwortlich. Beim Aufruf dieser Schnittstelle wird die Die lokale Bestellnummer wird ebenfalls gesendet. Dann erhalten Sie eine prepay_id und führen dann einen Algorithmus für die prepay_id und einige Parameter aus, um den entsprechenden Signaturwert zu erhalten, und geben ihn dann an das Front-End zurück, und dann kann das Front-End Rufen Sie die Zahlung gemäß diesen Rückgabewerten auf und Sie können bezahlen

        Wenn die Zahlung erfolgreich ist, ist dies in Ordnung, da die lokale Bestellnummer an WeChat übergeben wurde. Sie entspricht also der lokalen Bestellnummer, die an die Bestellung auf WeChat gebunden ist. Solange die Zahlung per Tencent-Bestellung erfolgt, erfolgt sie entspricht dem Abschluss der lokalen Bestellung.



1. Beantragen Sie ein Zertifikat und legen Sie den geheimen V3-Schlüssel fest

e215ad3e485c41948516b21db0454dbf.png

 2. Legen Sie die APPid-Kontoverwaltung fest

353dccc71a18461ea59ff890ff081d4b.png

 3. Maven-Adresse

        <dependency>
            <groupId>com.github.wechatpay-apiv3</groupId>
            <artifactId>wechatpay-apache-httpclient</artifactId>
            <version>0.4.7</version>
        </dependency>

4. Öffentliche Parameterschnittstelle

/**
 * 直连商户
 */
public interface DirectConnection {

    String NOTIFY_URL = ""; //支付成功后的回调地址
    String MCH_ID = ""; //商户号
    String MCH_SERIAL_NO = ""; //商户证书序列号
    String API_3KEY = "";   //V3密钥
    String APP_ID = "";     //小程序或者公众号的ApId
    String privateKey = "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDOsZnx5Nh4qK7O" +
            "vzbDOQu5UMtSdoZWyqOC+gFNVAB7aPAzQwgN7OAUt7G8synPRdovQ/l116dZ0ZiX"+
            "XQX3Le8/o5szRH6LxpqcpFMaZg2N/HOydyTMaHI0wnZIc9BXR8aaXl7uVQnydF40"+
            "FoWicge6vTCXOyjirTpS2PGKy9+hu0vx7GbX1NUDl2hNXkH54pdWn5eof1fnbh/V"+
            "45q/OS7d9qnpYfs1ff+0nA=="; // 商户证书序列号对应的证书秘钥

}

Der Zertifikatsschlüssel, der der Seriennummer des Händlerzertifikats entspricht, befindet sich  an dieser Stelle des heruntergeladenen Zertifikats:


bb485ad4bd2e4018a7e4eab0dddb767e.png

 

Das rote Kästchen ist der Zertifikatsschlüssel, der der Seriennummer des Händlerzertifikats entspricht:


28fc165bfdcb4540895fa84db76897d2.png

5. Werkzeuge bestellen

package com.example.demo.zhifu;

import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson2.JSONObject;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.exception.HttpCodeException;
import com.wechat.pay.contrib.apache.httpclient.exception.NotFoundException;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Base64;

/**
 * 直连商户的下单工具类
 * @author [email protected]
 */

public class PayUtil {

    private CloseableHttpClient httpClient;
    private CertificatesManager certificatesManager;
    private Verifier verifier;
    private PrivateKey merchantPrivateKey;

    {
        try {
            merchantPrivateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream(DirectConnection.privateKey.getBytes("utf-8")));
            // 获取证书管理器实例
            certificatesManager = CertificatesManager.getInstance();
            // 向证书管理器增加需要自动更新平台证书的商户信息
            certificatesManager.putMerchant(DirectConnection.MCH_ID, new WechatPay2Credentials(DirectConnection.MCH_ID,
                            new PrivateKeySigner(DirectConnection.MCH_SERIAL_NO, merchantPrivateKey)),
                    DirectConnection.API_3KEY.getBytes(StandardCharsets.UTF_8));
            // 从证书管理器中获取verifier
            verifier = certificatesManager.getVerifier(DirectConnection.MCH_ID);
            httpClient = WechatPayHttpClientBuilder.create()
                    .withMerchant(DirectConnection.MCH_ID, DirectConnection.MCH_SERIAL_NO, merchantPrivateKey)
                    .withValidator(new WechatPay2Validator(certificatesManager.getVerifier(DirectConnection.MCH_ID)))
                    .build();
        } catch (IOException | GeneralSecurityException | HttpCodeException | NotFoundException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 统一下单,获取到 prepay_id ,然后获取签名,调起
     * @param total
     * @param description
     * @return
     * @throws Exception
     */
    public String requestwxChatPay(String orderSn, int total, String description,String openid) throws Exception {

        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi");
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectMapper objectMapper = new ObjectMapper();
        //组合请求参数JSON格式
        ObjectNode rootNode = objectMapper.createObjectNode();
        rootNode.put("mchid", DirectConnection.MCH_ID)
                .put("appid", DirectConnection.APP_ID)
                .put("notify_url", DirectConnection.NOTIFY_URL + "returnNotify")
                .put("description", description)
                .put("out_trade_no", orderSn);
        rootNode.putObject("amount")
                // total:金额,以分为单位,假如是10块钱,那就要写 1000
                .put("total", total)
                .put("currency", "CNY");
        rootNode.putObject("payer")
                // openid:用户在该小程序或者公众号下的openid
                .put("openid", openid);
        try {
            objectMapper.writeValue(bos, rootNode);
            httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
            //获取预支付ID
            CloseableHttpResponse response = httpClient.execute(httpPost);
            String bodyAsString = EntityUtils.toString(response.getEntity());
            //微信成功响应
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == 200) {
                //时间戳
                String timestamp = System.currentTimeMillis() / 1000 + "";
                //随机字符串
                String nonce = RandomUtil.randomString(32);
                StringBuilder builder = new StringBuilder();

                // Appid
                builder.append(DirectConnection.APP_ID).append("\n");
                // 时间戳
                builder.append(timestamp).append("\n");
                // 随机字符串
                builder.append(nonce).append("\n");
                JsonNode jsonNode = objectMapper.readTree(bodyAsString);
                // 预支付会话ID
                builder.append("prepay_id=").append(jsonNode.get("prepay_id").textValue()).append("\n");
                //获取签名
                String sign = this.sign(builder.toString().getBytes("utf-8"), merchantPrivateKey);

                JSONObject jsonMap = new JSONObject();
                jsonMap.put("noncestr", nonce);
                jsonMap.put("timestamp", timestamp);
                jsonMap.put("prepayid", jsonNode.get("prepay_id").textValue());
                jsonMap.put("sign", sign);
                jsonMap.put("appid", DirectConnection.APP_ID);
                jsonMap.put("partnerid", DirectConnection.MCH_ID);

                return jsonMap.toJSONString();//响应签名数据,前端拿着响应数据调起微信SDK
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**

     /**
     * 计算签名
     *
     * @param message
     * @param yourPrivateKey
     * @return
     */
    private String sign(byte[] message, PrivateKey yourPrivateKey) {
        try {
            Signature sign = Signature.getInstance("SHA256withRSA");
            sign.initSign(yourPrivateKey);
            sign.update(message);
            return Base64.getEncoder().encodeToString(sign.sign());
        } catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) {
            e.printStackTrace();
        }
        return "";
    }

}

6. Callback-Signatur-Tool-Klasse

package com.example.demo.zhifu;

/**
 * @Description:
 * @Author sk
 * @Date: 2023/7/5 14:31
 */

import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;

/**
 * 回调签名配置
 * @author [email protected]
 */
public class AesUtil {
    static final int KEY_LENGTH_BYTE = 32;
    static final int TAG_LENGTH_BIT = 128;
    private final byte[] aesKey;

    public AesUtil(byte[] key) {
        if (key.length != KEY_LENGTH_BYTE) {
            throw new IllegalArgumentException("无效的ApiV3Key,长度必须为32个字节");
        }
        this.aesKey = key;
    }
    public String decryptToString(byte[] associatedData, byte[] nonce, String ciphertext) throws GeneralSecurityException, IOException {
        try {
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");

            SecretKeySpec key = new SecretKeySpec(aesKey, "AES");
            GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce);

            cipher.init(Cipher.DECRYPT_MODE, key, spec);
            cipher.updateAAD(associatedData);

            return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), "utf-8");
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            throw new IllegalStateException(e);
        } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
            throw new IllegalArgumentException(e);
        }
    }
}

7. Controller bestellen

package com.example.demo.zhifu;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import org.springframework.web.bind.annotation.*;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

/**
 * @Description:
 * @Author sk
 * @Date: 2023/7/5 19:10
 */
@RestController
    @RequestMapping(value = "/pay")
public class payController {



    /**
     * 预支付下单
     * @param orderSn 订单号
     * @param total 分
     * @param description 描述
     * @return
     */
    @GetMapping(value = "/getPay")
    public String getPay(String orderSn,int total , String description)
    {
        PayUtil payUtil = new PayUtil();
        try {
            return payUtil.requestwxChatPay(orderSn, total, description, "oYgFI91D00GpCwccdnKDR4KNxI4k");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

    // 支付回调
    @PostMapping(value = "/returnNotify")
    public Map returnNotify(@RequestBody JSONObject jsonObject)
    {
        // v3 私钥
        String key = "xxxxx";
        String json = jsonObject.toString();
        String associated_data = (String) JSONUtil.getByPath(JSONUtil.parse(json), "resource.associated_data");
        String ciphertext = (String) JSONUtil.getByPath(JSONUtil.parse(json), "resource.ciphertext");
        String nonce = (String) JSONUtil.getByPath(JSONUtil.parse(json), "resource.nonce");
        try {
            String decryptData = new AesUtil(key.getBytes(StandardCharsets.UTF_8)).decryptToString(associated_data.getBytes(StandardCharsets.UTF_8), nonce.getBytes(StandardCharsets.UTF_8), ciphertext);
            System.out.println("decryptData = " + decryptData);
            //TODO 业务校验

        } catch (Exception e) {
            e.printStackTrace();
        }

        HashMap<String, String> stringStringHashMap = new HashMap<>();
        stringStringHashMap.put("code","200");
        stringStringHashMap.put("message","返回成功");
        // 返回这个说明应答成功
        return stringStringHashMap;
    }

}

8. Offizielle Dokumente:

Einführung – Schnittstellenregeln | Dokumentationszentrum der WeChat Payment Merchant Platform

Supongo que te gusta

Origin blog.csdn.net/qq_26112725/article/details/131656139
Recomendado
Clasificación