From bbd9c436f23f5fdbe712c4a22d90b457066bdf38 Mon Sep 17 00:00:00 2001
From: k94314517 <8417338+k94314517@user.noreply.gitee.com>
Date: 星期四, 17 七月 2025 19:25:47 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master'
---
server/services/src/main/java/com/doumee/config/wx/TransferToUser.java | 312 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 312 insertions(+), 0 deletions(-)
diff --git a/server/services/src/main/java/com/doumee/config/wx/TransferToUser.java b/server/services/src/main/java/com/doumee/config/wx/TransferToUser.java
new file mode 100644
index 0000000..af0b96e
--- /dev/null
+++ b/server/services/src/main/java/com/doumee/config/wx/TransferToUser.java
@@ -0,0 +1,312 @@
+package com.doumee.config.wx;
+
+import com.alibaba.fastjson.JSONObject;
+import com.google.gson.annotations.Expose;
+import com.google.gson.annotations.SerializedName;
+import okhttp3.*;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ *
+ * @Author : Rk
+ * @create 2025/7/17 9:19
+ */
+public class TransferToUser {
+
+ private static String HOST = "https://api.mch.weixin.qq.com";
+ private static String METHOD = "POST";
+ private static String PATH = "/v3/fund-app/mch-transfer/transfer-bills";
+ private static String CANCEL_PATH = "/v3/fund-app/mch-transfer/transfer-bills/out-bill-no/{out_bill_no}/cancel";
+
+
+ public static void main(String[] args) {
+ // TODO: 璇峰噯澶囧晢鎴峰紑鍙戝繀瑕佸弬鏁帮紝鍙傝�冿細https://pay.weixin.qq.com/doc/v3/merchant/4013070756
+ TransferToUser client = new TransferToUser(
+ "1229817002", // 鍟嗘埛鍙凤紝鏄敱寰俊鏀粯绯荤粺鐢熸垚骞跺垎閰嶇粰姣忎釜鍟嗘埛鐨勫敮涓�鏍囪瘑绗︼紝鍟嗘埛鍙疯幏鍙栨柟寮忓弬鑰� https://pay.weixin.qq.com/doc/v3/merchant/4013070756
+ "3FE90C2F3D40A56E1C51926F31B8A8D22426CCE0", // 鍟嗘埛API璇佷功搴忓垪鍙凤紝濡備綍鑾峰彇璇峰弬鑰� https://pay.weixin.qq.com/doc/v3/merchant/4013053053
+ "d://wechatApiclient_key.pem", // 鍟嗘埛API璇佷功绉侀挜鏂囦欢璺緞锛屾湰鍦版枃浠惰矾寰�
+ "PUB_KEY_ID_0112298170022025071700291836000600", // 寰俊鏀粯鍏挜ID锛屽浣曡幏鍙栬鍙傝�� https://pay.weixin.qq.com/doc/v3/merchant/4013038816
+ "d://pub_key.pem" // 寰俊鏀粯鍏挜鏂囦欢璺緞锛屾湰鍦版枃浠惰矾寰�
+ );
+
+ TransferToUserRequest request = new TransferToUserRequest();
+ request.appid = "wxcd2b89fd2ff065f8"; //灏忕▼搴廼d
+ request.outBillNo = "plfk2020042016";
+ request.transferSceneId = "1005";
+ request.openid = "oKKHU5IFKpss_DIbFX1lqghFJOEg";
+ request.userName = client.encrypt("鏂芥棴杈�");
+ request.transferAmount = 91L;
+ request.transferRemark = "~~~";
+ request.notifyUrl = "https://www.weixin.qq.com/wxpay/pay.php";
+ request.userRecvPerception = "鍔冲姟鎶ラ叕";
+ request.transferSceneReportInfos = new ArrayList<>();
+ {
+ TransferSceneReportInfo item0 = new TransferSceneReportInfo();
+ item0.infoType = "宀椾綅绫诲瀷";
+ item0.infoContent = "瀹屾垚璁㈠崟";
+ request.transferSceneReportInfos.add(item0);
+ TransferSceneReportInfo item1 = new TransferSceneReportInfo();
+ item1.infoType = "鎶ラ叕璇存槑";
+ item1.infoContent = "璁㈠崟鎶ラ叕";
+ request.transferSceneReportInfos.add(item1);
+ };
+ try {
+ TransferToUserResponse response = client.testRun(request);
+
+ // TODO: 璇锋眰鎴愬姛锛岀户缁笟鍔¢�昏緫
+ System.out.println(JSONObject.toJSONString(response));
+ } catch (WXPayUtility.ApiException e) {
+ // TODO: 璇锋眰澶辫触锛屾牴鎹姸鎬佺爜鎵ц涓嶅悓鐨勯�昏緫
+ e.printStackTrace();
+ }
+ }
+
+ private final String mchid;
+ private final String certificateSerialNo;
+ private final PrivateKey privateKey;
+ private final String wechatPayPublicKeyId;
+ private final PublicKey wechatPayPublicKey;
+
+ public TransferToUser(String mchid, String certificateSerialNo, String privateKeyFilePath, String wechatPayPublicKeyId, String wechatPayPublicKeyFilePath) {
+ this.mchid = mchid;
+ this.certificateSerialNo = certificateSerialNo;
+ this.privateKey = WXPayUtility.loadPrivateKeyFromPath(privateKeyFilePath);
+ this.wechatPayPublicKeyId = wechatPayPublicKeyId;
+ this.wechatPayPublicKey = WXPayUtility.loadPublicKeyFromPath(wechatPayPublicKeyFilePath);
+ }
+
+ public TransferToUserResponse testRun(TransferToUserRequest request) {
+ String uri = PATH;
+ String reqBody = WXPayUtility.toJson(request);
+
+ Request.Builder reqBuilder = new Request.Builder().url(HOST + uri);
+ reqBuilder.addHeader("Accept", "application/json");
+ reqBuilder.addHeader("Wechatpay-Serial", wechatPayPublicKeyId);
+ reqBuilder.addHeader("Authorization", WXPayUtility.buildAuthorization(mchid, certificateSerialNo,privateKey, METHOD, uri, reqBody));
+ reqBuilder.addHeader("Content-Type", "application/json");
+ RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), reqBody);
+ reqBuilder.method(METHOD, requestBody);
+ Request httpRequest = reqBuilder.build();
+
+ // 鍙戦�丠TTP璇锋眰
+ OkHttpClient client = new OkHttpClient.Builder().build();
+ try (Response httpResponse = client.newCall(httpRequest).execute()) {
+ String respBody = WXPayUtility.extractBody(httpResponse);
+ if (httpResponse.code() >= 200 && httpResponse.code() < 300) {
+ // 2XX 鎴愬姛锛岄獙璇佸簲绛旂鍚�
+ WXPayUtility.validateResponse(this.wechatPayPublicKeyId, this.wechatPayPublicKey,
+ httpResponse.headers(), respBody);
+
+ // 浠嶩TTP搴旂瓟鎶ユ枃鏋勫缓杩斿洖鏁版嵁
+ return WXPayUtility.fromJson(respBody, TransferToUserResponse.class);
+ } else {
+ throw new WXPayUtility.ApiException(httpResponse.code(), respBody, httpResponse.headers());
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException("Sending request to " + uri + " failed.", e);
+ }
+ }
+
+ public TransferToUserResponse run(TransferToUserRequest request,String name) {
+ String uri = PATH;
+ request.appid = WxMiniConfig.wxProperties.getSubAppId();
+ request.transferSceneId = "1005";
+ request.userRecvPerception = "鍔冲姟鎶ラ叕";
+ if(request.transferAmount >= 30){
+ request.userName = this.encrypt(name);
+ }
+
+ request.transferSceneReportInfos = new ArrayList<>();
+ {
+ TransferSceneReportInfo item0 = new TransferSceneReportInfo();
+ item0.infoType = "宀椾綅绫诲瀷";
+ item0.infoContent = "瀹屾垚璁㈠崟";
+ request.transferSceneReportInfos.add(item0);
+ TransferSceneReportInfo item1 = new TransferSceneReportInfo();
+ item1.infoType = "鎶ラ叕璇存槑";
+ item1.infoContent = "璁㈠崟鎶ラ叕";
+ request.transferSceneReportInfos.add(item1);
+ };
+ String reqBody = WXPayUtility.toJson(request);
+
+ Request.Builder reqBuilder = new Request.Builder().url(HOST + uri);
+ reqBuilder.addHeader("Accept", "application/json");
+ reqBuilder.addHeader("Wechatpay-Serial", WxMiniConfig.wxProperties.getWechatPayPublicKeyId());
+ reqBuilder.addHeader("Authorization", WXPayUtility.buildAuthorization(WxMiniConfig.wxProperties.getMchId(),
+ WxMiniConfig.wxProperties.getWechatSerialNumer(),
+ WXPayUtility.loadPrivateKeyFromPath(WxMiniConfig.wxProperties.getWechatPrivateKeyPath()),
+ METHOD, uri, reqBody));
+
+// reqBuilder.addHeader("Authorization", WXPayUtility.buildAuthorization(mchid, certificateSerialNo,privateKey, METHOD, uri, reqBody));
+ reqBuilder.addHeader("Content-Type", "application/json");
+ RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), reqBody);
+ reqBuilder.method(METHOD, requestBody);
+ Request httpRequest = reqBuilder.build();
+
+ // 鍙戦�丠TTP璇锋眰
+ OkHttpClient client = new OkHttpClient.Builder().build();
+ try (Response httpResponse = client.newCall(httpRequest).execute()) {
+ String respBody = WXPayUtility.extractBody(httpResponse);
+ if (httpResponse.code() >= 200 && httpResponse.code() < 300) {
+ // 2XX 鎴愬姛锛岄獙璇佸簲绛旂鍚�
+ WXPayUtility.validateResponse(WxMiniConfig.wxProperties.getWechatPayPublicKeyId(), WXPayUtility.loadPublicKeyFromPath(WxMiniConfig.wxProperties.getWechatPubKeyPath()),
+ httpResponse.headers(), respBody);
+
+ // 浠嶩TTP搴旂瓟鎶ユ枃鏋勫缓杩斿洖鏁版嵁
+ return WXPayUtility.fromJson(respBody, TransferToUserResponse.class);
+ } else {
+ throw new WXPayUtility.ApiException(httpResponse.code(), respBody, httpResponse.headers());
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException("Sending request to " + uri + " failed.", e);
+ }
+ }
+
+
+ public TransferToUser.CancelTransferResponse cancelRun(TransferToUser.CancelTransferRequest request) {
+ String uri = CANCEL_PATH;
+ uri = uri.replace("{out_bill_no}", WXPayUtility.urlEncode(request.outBillNo));
+
+ Request.Builder reqBuilder = new Request.Builder().url(HOST + uri);
+ reqBuilder.addHeader("Accept", "application/json");
+ reqBuilder.addHeader("Wechatpay-Serial", wechatPayPublicKeyId);
+ reqBuilder.addHeader("Authorization", WXPayUtility.buildAuthorization(WxMiniConfig.wxProperties.getMchId(),
+ WxMiniConfig.wxProperties.getWechatSerialNumer(),
+ WXPayUtility.loadPrivateKeyFromPath(WxMiniConfig.wxProperties.getWechatPrivateKeyPath()),
+ METHOD, uri, null));
+ reqBuilder.addHeader("Content-Type", "application/json");
+ RequestBody emptyBody = RequestBody.create(null, "");
+ reqBuilder.method(METHOD, emptyBody);
+ Request httpRequest = reqBuilder.build();
+
+ // 鍙戦�丠TTP璇锋眰
+ OkHttpClient client = new OkHttpClient.Builder().build();
+ try (Response httpResponse = client.newCall(httpRequest).execute()) {
+ String respBody = WXPayUtility.extractBody(httpResponse);
+ if (httpResponse.code() >= 200 && httpResponse.code() < 300) {
+ // 2XX 鎴愬姛锛岄獙璇佸簲绛旂鍚�
+ WXPayUtility.validateResponse(WxMiniConfig.wxProperties.getWechatPayPublicKeyId(), WXPayUtility.loadPublicKeyFromPath(WxMiniConfig.wxProperties.getWechatPubKeyPath()),
+ httpResponse.headers(), respBody);
+
+ // 浠嶩TTP搴旂瓟鎶ユ枃鏋勫缓杩斿洖鏁版嵁
+ return WXPayUtility.fromJson(respBody, TransferToUser.CancelTransferResponse.class);
+ } else {
+ throw new WXPayUtility.ApiException(httpResponse.code(), respBody, httpResponse.headers());
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException("Sending request to " + uri + " failed.", e);
+ }
+ }
+
+
+
+ public String encrypt(String plainText) {
+ return WXPayUtility.encrypt(this.wechatPayPublicKey, plainText);
+ }
+
+ public static class TransferToUserResponse {
+ @SerializedName("out_bill_no")
+ public String outBillNo;
+
+ @SerializedName("transfer_bill_no")
+ public String transferBillNo;
+
+ @SerializedName("create_time")
+ public String createTime;
+
+ @SerializedName("state")
+ public TransferBillStatus state;
+
+ @SerializedName("package_info")
+ public String packageInfo;
+ }
+
+ public enum TransferBillStatus {
+ @SerializedName("ACCEPTED")
+ ACCEPTED,
+ @SerializedName("PROCESSING")
+ PROCESSING,
+ @SerializedName("WAIT_USER_CONFIRM")
+ WAIT_USER_CONFIRM,
+ @SerializedName("TRANSFERING")
+ TRANSFERING,
+ @SerializedName("SUCCESS")
+ SUCCESS,
+ @SerializedName("FAIL")
+ FAIL,
+ @SerializedName("CANCELING")
+ CANCELING,
+ @SerializedName("CANCELLED")
+ CANCELLED
+ }
+
+ public static class TransferSceneReportInfo {
+ @SerializedName("info_type")
+ public String infoType;
+
+ @SerializedName("info_content")
+ public String infoContent;
+ }
+
+ public static class TransferToUserRequest {
+
+ @SerializedName("appid")
+ public String appid;
+
+ @SerializedName("out_bill_no")
+ public String outBillNo;
+
+ @SerializedName("transfer_scene_id")
+ public String transferSceneId;
+
+ @SerializedName("openid")
+ public String openid;
+
+ @SerializedName("user_name")
+ public String userName;
+
+ @SerializedName("transfer_amount")
+ public Long transferAmount;
+
+ @SerializedName("transfer_remark")
+ public String transferRemark;
+
+ @SerializedName("notify_url")
+ public String notifyUrl;
+
+ @SerializedName("user_recv_perception")
+ public String userRecvPerception;
+
+ @SerializedName("transfer_scene_report_infos")
+ public List<TransferSceneReportInfo> transferSceneReportInfos;
+ }
+
+ public static class CancelTransferResponse {
+ @SerializedName("out_bill_no")
+ public String outBillNo;
+
+ @SerializedName("transfer_bill_no")
+ public String transferBillNo;
+
+ @SerializedName("state")
+ public String state;
+
+ @SerializedName("update_time")
+ public String updateTime;
+ }
+
+ public static class CancelTransferRequest {
+ @SerializedName("out_bill_no")
+ @Expose(serialize = false)
+ public String outBillNo;
+ }
+
+}
--
Gitblit v1.9.3