WeChat アプレット アクセス WeChat 支払いプロセスのメモ

準備工程

「通常のマーチャントアカウント」(マーチャントプラットフォームにログイン後、ホームページ、トランザクションセンター、アカウントセンター、マーケティングセンター、製品センター、データセンターがあります)を使用し、製品センターでJSAPI支払いを開き、マーチャントを記録します番号。

  • 次のように入力します: アカウント センター - API セキュリティ - API 証明書の申請 - 証明書の管理、プロンプトに従って証明書を追加し、追加後の証明書のシリアル番号を記録します。
  • 「アカウント センター - API セキュリティ - APIv3 キーを設定し、変更し、プロンプトに従うと、キーが自動的に生成されます (最大長 32 文字の文字列)。キーを記録します。」
  • 入力: アカウント センター - API セキュリティ - API キーを設定、変更、上記と同じ (このステップは使用されない場合があります)
  • 「Product Center - AppID Account Management - Associate AppID」と入力し、関連付ける必要がある AppID (小規模プログラム、公式アカウントなど) を入力します。対応する AppID プラットフォームにログインし、協会を通じて申請します。ミニプログラム プラットフォームの操作パスは次のとおりです: 機能 - WeChat 支払い

さらに、準備する必要があるのは、

  • アプレットの appId
  • アプレットアプリの秘密

先に読んでください:

正式なプロセス

ログイン

  1. フロントエンド:このインターフェイスを呼び出して承認を取得します。
  2. フロントエンド:成功後にこのインターフェイスを呼び出します。バックエンドによって提供される WeChat ログイン インターフェイスにパラメーター: コードを渡し、同時に前のインターフェイスによって取得された他の情報も提供します。
  3. バックエンド: WeChat ログインしてコードを受信した後、このインターフェイスを呼び出し、アプレットの appId と appSecret + 取得したコードを使用してユーザー情報 (openid Unionid session_key) の取得を要求し、保存します。取得に成功した後、 openidは登録されていますので、未登録の場合はパスワードなしでログインしてください。

支払う

  1. フロントエンド: 注文リクエストをバックエンド インターフェイスに送信し、注文 UUID を送信します。
  2. 後部:
    1. このインターフェイスを呼び出し、フロントエンドによって送信された UUID として販売者の注文番号を設定し、注文リクエストを実行して、prepay_id を取得します。
    2. この prepay_id を使用して、ステップ 3 で必要なリクエスト パラメーターを生成し、フロント エンドに返します。このステップには手動署名がここから署名ツールをダウンロードして署名結果が正しいかどうかを比較できます。
  3. フロントエンド:
    1. このインターフェイスを呼び出し、wx.requestPayment(OBJECT) を呼び出して WeChat 支払いを開始します。リクエスト パラメーターはバックエンドによって生成され、前のリクエストでフロントエンドに返される必要があります。
    2. ユーザーはプロンプトに従って支払いを行います(このシステムのプロセスではありません)。
  4. バックエンド: WeChat サーバーは、ステップ 2 の Notice_url フィールドで指定されたインターフェイスに支払い結果を送信します。このインターフェイスで完了するステップは次 のとおりです。
    1. 署名検証については、公式SDKのプラットフォーム証明書自動更新機能を利用すると、ScheduledUpdateCertificatesVerifierクラスのgetlatestCertificate()メソッドで最新のプラットフォーム証明書(X509Certificateクラス)を直接取得できます。
    2. 本体オブジェクトを復号化する
    3. 復号結果に応じたシステム上の業務(注文の決済状況の変更等)

Javaコードの一部

AppIdなどの設定情報(実行設定ファイルに設定)

@Repository
@ConfigurationProperties(prefix = "wx")
@Data
public class WxConfig {
    
    
    String appId;
    String appSecret;
    /**
     * 商户号Id
     */
    String mchId;
    /**
     * 证书序列号
     */
    String serialNo;
    /**
     * apiV3 私钥
     */
    String apiV3Key;
    /**
     * api私钥
     */
    String apiKey;
    /**
     * 认证类型
     */
    String authType;
    /**
     * 商户私钥文件路径
     */
    String privateKeyPath;

}

署名と署名検証ツール

public class SignUtils {
    
    
    public static String buildMessage(String... message) {
    
    
        return String.join("\n", message) + "\n";
    }

    public static String sign(PrivateKey privateKey, String message)
            throws SignatureException, NoSuchAlgorithmException, InvalidKeyException {
    
    
        return sign(privateKey, message.getBytes(StandardCharsets.UTF_8));
    }

    public static String sign(PrivateKey privateKey, byte[] message)
            throws SignatureException, NoSuchAlgorithmException, InvalidKeyException {
    
    
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(privateKey);
        sign.update(message);
        return Base64.getEncoder().encodeToString(sign.sign());
    }

    public static String sign(PrivateKey privateKey, String... message)
            throws SignatureException, NoSuchAlgorithmException, InvalidKeyException {
    
    
        return sign(privateKey, buildMessage(message));
    }

    public static boolean check(X509Certificate certificate, String timestamp, String nonce, Object requestBody, String signature)
            throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
    
    
        //构造验签名串
        String signatureStr = buildMessage(timestamp, nonce, JSONObject.toJSONString(requestBody));
        System.out.println("signatureStr = " + signatureStr);
        // 加载SHA256withRSA签名器
        Signature signer = Signature.getInstance("SHA256withRSA");
        // 用微信平台公钥对签名器进行初始化(调上一节中的获取平台证书方法)
        signer.initVerify(certificate);
        // 把我们构造的验签名串更新到签名器中
        signer.update(signatureStr.getBytes(StandardCharsets.UTF_8));
        return signer.verify(Base64Utils.decodeFromString(signature));
    }

    public static boolean check(X509Certificate certificate, HttpServletRequest httpServletRequest, Object requestBody)
            throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
    
    
        String signature = httpServletRequest.getHeader("Wechatpay-Signature");
        String timestamp = httpServletRequest.getHeader("Wechatpay-Timestamp");
        String nonce = httpServletRequest.getHeader("Wechatpay-Nonce");

        return check(certificate, timestamp, nonce, requestBody, signature);
    }
}

メッセージ復号化ツール

public class AesDecrypt {
    
    
    static final int KEY_LENGTH_BYTE = 32;
    static final int TAG_LENGTH_BIT = 128;

    private final byte[] apiV3Key;


    public AesDecrypt(byte[] apiV3Key) {
    
    
        if (apiV3Key.length != KEY_LENGTH_BYTE) {
    
    
            throw new IllegalArgumentException("无效的ApiV3Key,长度必须为32个字节");
        }
        this.apiV3Key = apiV3Key;
    }

    public AesDecrypt(String apiV3Key) {
    
    
        this(apiV3Key.getBytes());
    }

    public String decryptToString(String associatedData, String nonce, String ciphertext)
            throws GeneralSecurityException {
    
    
        return decryptToString(
                associatedData.getBytes(StandardCharsets.UTF_8),
                nonce.getBytes(StandardCharsets.UTF_8),
                ciphertext
        );
    }

    public String decryptToString(byte[] associatedData, byte[] nonce, String ciphertext)
            throws GeneralSecurityException {
    
    
        try {
    
    
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");

            SecretKeySpec key = new SecretKeySpec(apiV3Key, "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)), StandardCharsets.UTF_8);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
    
    
            throw new IllegalStateException(e);
        } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
    
    
            throw new IllegalArgumentException(e);
        }
    }
}

いくつかのユーティリティ Bean

@Configuration
@Slf4j
public class WxConfiguration {
    
    

    @Bean
    public PrivateKey merchantPrivateKey(WxConfig wxConfig) throws FileNotFoundException {
    
    
        log.info("加载私钥");
        File file = new File(wxConfig.getPrivateKeyPath());
        System.out.println("file.exists() = " + file.exists());
        return PemUtil.loadPrivateKey(new FileInputStream(wxConfig.getPrivateKeyPath()));
    }

    @Bean
    public ScheduledUpdateCertificatesVerifier verifier(WxConfig wxConfig, PrivateKey merchantPrivateKey) {
    
    
        log.info("加载验证器");
        return new ScheduledUpdateCertificatesVerifier(
                new WechatPay2Credentials(wxConfig.getMchId(), new PrivateKeySigner(wxConfig.getSerialNo(), merchantPrivateKey)),
                wxConfig.getApiV3Key().getBytes(StandardCharsets.UTF_8));
    }

    @Bean
    public AesDecrypt aesDecrypt(WxConfig wxConfig) {
    
    
        log.info("加载解密器");
        return new AesDecrypt(wxConfig.getApiV3Key());
    }
}

おすすめ

転載: blog.csdn.net/hjg719/article/details/121856463