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" // 微信支付公钥文件路径,本地文件路径
|
// );
|
|
TransferToUser client = new TransferToUser(
|
"1661772003", // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756
|
"3C9A32FB6CD453FAAAF97F9737ECAEA9D6625727", // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053
|
"d://wechatApiclient_key.pem", // 商户API证书私钥文件路径,本地文件路径
|
"47E172124E73E8098A565E971064C20ACDE7C911", // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816
|
"d://wechatpay_47E172124E73E8098A565E971064C20ACDE7C911.pem" // 微信支付公钥文件路径,本地文件路径
|
);
|
|
TransferToUserRequest request = new TransferToUserRequest();
|
request.appid = "wxcd2b89fd2ff065f8"; //小程序id
|
request.outBillNo = "plfk2020042016";
|
request.transferSceneId = "1005";
|
request.openid = "oKKHU5IFKpss_DIbFX1lqghFJOEg";
|
// request.userName = client.encrypt("施旭辉");
|
request.transferAmount = 10L;
|
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();
|
|
// 发送HTTP请求
|
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);
|
|
// 从HTTP应答报文构建返回数据
|
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.notifyUrl = WxMiniConfig.wxProperties.getWechatNotifyUrl();
|
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.getSubMchId(),
|
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();
|
|
// 发送HTTP请求
|
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);
|
|
// 从HTTP应答报文构建返回数据
|
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.getSubMchId(),
|
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();
|
|
// 发送HTTP请求
|
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);
|
|
// 从HTTP应答报文构建返回数据
|
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;
|
}
|
|
}
|