🆕 #2443【微信支付】增加服务商微工卡相关功能接口以及微信批量转账到零钱的服务商接口实现

Co-authored-by: xiaoqiang <dengmingqiang@youmengbang.com>
This commit is contained in:
xiaoguaiYJ
2021-12-14 12:41:48 +08:00
committed by GitHub
parent 6f35b985b1
commit c1d0b68d32
36 changed files with 5099 additions and 1 deletions

View File

@@ -0,0 +1,201 @@
package com.github.binarywang.wxpay.service;
import com.github.binarywang.wxpay.bean.ecommerce.FundBalanceResult;
import com.github.binarywang.wxpay.bean.ecommerce.enums.SpAccountTypeEnum;
import com.github.binarywang.wxpay.bean.marketing.transfer.*;
import com.github.binarywang.wxpay.exception.WxPayException;
import javax.crypto.BadPaddingException;
import java.io.InputStream;
/**
* 微信批量转账到零钱【V3接口】服务商API
*
* @author xiaoqiang
* @date 2021-12-06
*/
public interface PartnerTransferService {
/**
* 发起批量转账API
* 适用对象:服务商
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter3_1.shtml
* 请求URLhttps://api.mch.weixin.qq.com/v3/partner-transfer/batches
* 请求方式POST
* 接口限频:单个服务商 50QPS如果超过频率限制会报错FREQUENCY_LIMITED请降低频率请求。
*
* @param request 请求对象
* @return 返回数据 fund balance result
* @throws WxPayException the wx pay exception
*/
PartnerTransferResult batchTransfer(PartnerTransferRequest request) throws WxPayException;
/**
* 微信支付批次单号查询批次单API
* 接口说明
* 适用对象:服务商
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter3_2.shtml
* 请求URLhttps://api.mch.weixin.qq.com/v3/partner-transfer/batches/batch-id/{batch_id}
* 请求方式GET
* 接口限频:单个服务商 50QPS如果超过频率限制会报错FREQUENCY_LIMITED请降低频率请求。
*
* @param request 请求对象
* @return 返回数据 fund balance result
* @throws WxPayException the wx pay exception
*/
BatchNumberResult queryBatchByBatchId(BatchNumberRequest request) throws WxPayException;
/**
* 微信支付明细单号查询明细单API
* 接口说明
* 适用对象:服务商
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter3_3.shtml
* 请求URLhttps://api.mch.weixin.qq.com/v3/partner-transfer/batches/batch-id/{batch_id}/details/detail-id/{detail_id}
* 请求方式GET
* 接口限频:单个服务商 50QPS如果超过频率限制会报错FREQUENCY_LIMITED请降低频率请求。
*
* @param batchId 微信批次单号
* @param detailId 微信明细单号
* @return 返回数据 fund balance result
* @throws WxPayException the wx pay exception
* @throws BadPaddingException the wx decrypt exception
*/
BatchDetailsResult queryBatchDetailByWeChat(String batchId, String detailId) throws WxPayException, BadPaddingException;
/**
* 商家批次单号查询批次单API
* 接口说明
* 适用对象:服务商
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter3_4.shtml
* 请求URLhttps://api.mch.weixin.qq.com/v3/partner-transfer/batches/out-batch-no/{out_batch_no}
* 请求方式GET
* 接口限频:单个服务商 50QPS如果超过频率限制会报错FREQUENCY_LIMITED请降低频率请求。
*
* @param request 请求对象
* @return 返回数据 fund balance result
* @throws WxPayException the wx pay exception
*/
BatchNumberResult queryBatchByOutBatchNo(MerchantBatchRequest request) throws WxPayException;
/**
* 商家明细单号查询明细单API
* 接口说明
* 适用对象:服务商
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter3_5.shtml
* 请求URLhttps://api.mch.weixin.qq.com/v3/partner-transfer/batches/out-batch-no/{out_batch_no}/details/out-detail-no/{out_detail_no}
* 请求方式GET
* 接口限频:单个服务商 50QPS如果超过频率限制会报错FREQUENCY_LIMITED请降低频率请求。
*
* @param outBatchNo 商家明细单号
* @param outDetailNo 商家批次单号
* @return 返回数据 fund balance result
* @throws WxPayException the wx pay exception
* @throws BadPaddingException the wx decrypt exception
*/
BatchDetailsResult queryBatchDetailByMch(String outBatchNo, String outDetailNo) throws WxPayException, BadPaddingException;
/**
* 转账电子回单申请受理API
* 接口说明
* 适用对象:直连商户 服务商
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter4_1.shtml
* 请求URLhttps://api.mch.weixin.qq.com/v3/transfer/bill-receipt
* 请求方式POST
*
* @param request 商家批次单号
* @return 返回数据 fund balance result
* @throws WxPayException the wx pay exception
*/
BillReceiptResult receiptBill(ReceiptBillRequest request) throws WxPayException;
/**
* 查询转账电子回单API
* 接口说明
* 适用对象:直连商户 服务商
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter4_2.shtml
* 请求URLhttps://api.mch.weixin.qq.com/v3/transfer/bill-receipt/{out_batch_no}
* 请求方式GET
*
* @param outBatchNo 商家批次单号
* @return 返回数据 fund balance result
* @throws WxPayException the wx pay exception
*/
BillReceiptResult queryBillReceipt(String outBatchNo) throws WxPayException;
/**
* 转账明细电子回单受理API
* 接口说明
* 适用对象:直连商户 服务商
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter4_4.shtml
* 请求URLhttps://api.mch.weixin.qq.com/v3/transfer-detail/electronic-receipts
* 请求方式POST
* 前置条件只支持受理最近90天内的转账明细单
*
* @param request 请求对象
* @return 返回数据 fund balance result
* @throws WxPayException the wx pay exception
*/
ElectronicReceiptsResult transferElectronic(ElectronicReceiptsRequest request) throws WxPayException;
/**
* 查询转账明细电子回单受理结果API
* 接口说明
* 适用对象:直连商户 服务商
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter4_5.shtml
* 请求URLhttps://api.mch.weixin.qq.com/v3/transfer-detail/electronic-receipts
* 请求方式GET
* 前置条件只支持查询最近90天内的转账明细单
*
* @param request 请求对象
* @return 返回数据 fund balance result
* @throws WxPayException the wx pay exception
*/
ElectronicReceiptsResult queryTransferElectronicResult(ElectronicReceiptsRequest request) throws WxPayException;
/**
* 下载电子回单API
* 接口说明
* 适用对象:直连商户 服务商
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter4_3.shtml
* 请求URL通过申请账单接口获取到“download_url”URL有效期10min
* 请求方式GET
* 前置条件调用申请账单接口并获取到“download_url”
*
* @param url 微信返回的电子回单地址。
* @return 返回数据 fund balance result
* @throws WxPayException the wx pay exception
*/
InputStream transferDownload(String url) throws WxPayException;
/**
* <pre>
* 查询账户实时余额API
* 接口说明
* 适用对象:直连商户 服务商
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter5_1.shtml
* 请求URLhttps://api.mch.weixin.qq.com/v3/merchant/fund/balance/{account_type}
* 请求方式GET
* </pre>
*
* @param accountType 服务商账户类型 {@link SpAccountTypeEnum}
* @return 返回数据 fund balance result
* @throws WxPayException the wx pay exception
*/
FundBalanceResult fundBalance(SpAccountTypeEnum accountType) throws WxPayException;
/**
* <pre>
* 服务商账户日终余额
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter5_2.shtml
* 文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pages/amount.shtml
* </pre>
*
* @param accountType 服务商账户类型
* @param date 查询日期 2020-09-11
* @return 返回数据 fund balance result
* @throws WxPayException the wx pay exception
*/
FundBalanceResult spDayEndBalance(SpAccountTypeEnum accountType, String date);
}

View File

@@ -0,0 +1,103 @@
package com.github.binarywang.wxpay.service;
import com.github.binarywang.wxpay.bean.marketing.payroll.*;
import com.github.binarywang.wxpay.exception.WxPayException;
/**
* 微工卡-对接微信api
*
* @author xiaoqiang
* @date 2021/12/7 14:26
*/
public interface PayrollService {
/**
* 生成授权token
* 适用对象:服务商
* 请求URLhttps://api.mch.weixin.qq.com/v3/payroll-card/tokens
* 请求方式POST
*
* @param request 请求参数
* @return 返回数据
* @throws WxPayException the wx pay exception
*/
TokensResult payrollCardTokens(TokensRequest request) throws WxPayException;
/**
* 查询微工卡授权关系API
* 适用对象:服务商
* 请求URLhttps://api.mch.weixin.qq.com/v3/payroll-card/relations/{openid}
* 请求方式GET
*
* @param request 请求参数
* @return 返回数据
* @throws WxPayException the wx pay exception
*/
RelationsResult payrollCardRelations(RelationsRequest request) throws WxPayException;
/**
* 微工卡核身预下单API
* 适用对象:服务商
* 请求URLhttps://api.mch.weixin.qq.com/v3/payroll-card/authentications/pre-order
* 请求方式POST
*
* @param request 请求参数
* @return 返回数据
* @throws WxPayException the wx pay exception
*/
PreOrderResult payrollCardPreOrder(PreOrderRequest request) throws WxPayException;
/**
* 获取核身结果API
* 适用对象:服务商
* 请求URLhttps://api.mch.weixin.qq.com/v3/payroll-card/authentications/{authenticate_number}
* 请求方式GET
*
* @param subMchid 子商户号
* @param authenticateNumber 商家核身单号
* @return 返回数据
* @throws WxPayException the wx pay exception
*/
AuthenticationsResult payrollCardAuthenticationsNumber(String subMchid, String authenticateNumber) throws WxPayException;
/**
* 查询核身记录API
* 适用对象:服务商
* 请求URLhttps://api.mch.weixin.qq.com/v3/payroll-card/authentications
* 请求方式GET
*
* @param request 请求参数
* @return 返回数据
* @throws WxPayException the wx pay exception
*/
AuthRecordResult payrollCardAuthentications(AuthRecordRequest request) throws WxPayException;
/**
* 微工卡核身预下单(流程中完成授权)
* 适用对象:服务商
* 请求URLhttps://api.mch.weixin.qq.com/v3/payroll-card/authentications/pre-order-with-auth
* 请求方式POST
*
* @param request 请求参数
* @return 返回数据
* @throws WxPayException the wx pay exception
*/
PreOrderWithAuthResult payrollCardPreOrderWithAuth(PreOrderWithAuthRequest request) throws WxPayException;
/**
* 按日下载提现异常文件API
* 适用对象:服务商
* 请求URLhttps://api.mch.weixin.qq.com/v3/merchant/fund/withdraw/bill-type/{bill_type}
* 请求方式GET
*
* @param billType 账单类型
* NO_SUCC提现异常账单包括提现失败和提现退票账单。
* 示例值NO_SUCC
* @param billDate 账单日期 表示所在日期的提现账单格式为YYYY-MM-DD。
* 例如2008-01-01日发起的提现2008-01-03日银行返回提现失败则该提现数据将出现在bill_date为2008-01-03日的账单中。
* 示例值2019-08-17
* @return 返回数据
* @throws WxPayException the wx pay exception
*/
PreOrderWithAuthResult merchantFundWithdrawBillType(String billType, String billDate) throws WxPayException;
}

View File

@@ -179,6 +179,20 @@ public interface WxPayService {
*/
WxEntrustPapService getWxEntrustPapService();
/**
* 获取批量转账到零钱服务类.
*
* @return the Batch transfer to change service
*/
PartnerTransferService getPartnerTransferService();
/**
* 微工卡
*
* @return the micro card
*/
PayrollService getPayrollService();
/**
* 获取企业付款服务类.
*

View File

@@ -75,7 +75,8 @@ public abstract class BaseWxPayServiceImpl implements WxPayService {
private final MarketingFavorService marketingFavorService = new MarketingFavorServiceImpl(this);
private final MarketingBusiFavorService marketingBusiFavorService = new MarketingBusiFavorServiceImpl(this);
private final WxEntrustPapService wxEntrustPapService = new WxEntrustPapServiceImpl(this);
private final PartnerTransferService partnerTransferService = new PartnerTransferServiceImpl(this);
private final PayrollService payrollService = new PayrollServiceImpl(this);
protected Map<String, WxPayConfig> configMap;
@@ -144,6 +145,12 @@ public abstract class BaseWxPayServiceImpl implements WxPayService {
return wxEntrustPapService;
}
@Override
public PartnerTransferService getPartnerTransferService(){return partnerTransferService;}
@Override
public PayrollService getPayrollService(){return payrollService;}
@Override
public WxPayConfig getConfig() {
if (this.configMap.size() == 1) {

View File

@@ -0,0 +1,311 @@
package com.github.binarywang.wxpay.service.impl;
import com.github.binarywang.wxpay.bean.ecommerce.FundBalanceResult;
import com.github.binarywang.wxpay.bean.ecommerce.enums.SpAccountTypeEnum;
import com.github.binarywang.wxpay.bean.marketing.transfer.*;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.PartnerTransferService;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.v3.util.RsaCryptoUtil;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import jodd.util.StringUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import java.io.InputStream;
/**
* 批量转账到零钱(服务商)
*
* @author xiaoqiang
* @date 2021-12-06
*/
@Slf4j
@RequiredArgsConstructor
public class PartnerTransferServiceImpl implements PartnerTransferService {
private static final Gson GSON = new GsonBuilder().create();
private final WxPayService payService;
/**
* 发起批量转账API
* 适用对象:服务商
* 请求URLhttps://api.mch.weixin.qq.com/v3/partner-transfer/batches
* 请求方式POST
* 接口限频:单个服务商 50QPS如果超过频率限制会报错FREQUENCY_LIMITED请降低频率请求。
*
* @param request 请求对象
* @return 返回数据 {@link PartnerTransferResult}
* @throws WxPayException the wx pay exception
*/
@Override
public PartnerTransferResult batchTransfer(PartnerTransferRequest request) throws WxPayException {
request.getTransferDetailList().stream().forEach(p -> {
try {
String userName = RsaCryptoUtil.encryptOAEP(p.getUserName(), this.payService.getConfig().getVerifier().getValidCertificate());
p.setUserName(userName);
} catch (IllegalBlockSizeException e) {
throw new RuntimeException("姓名转换异常!", e);
}
});
String url = String.format("%s/v3/partner-transfer/batches", this.payService.getPayBaseUrl());
String response = this.payService.postV3WithWechatpaySerial(url, GSON.toJson(request));
return GSON.fromJson(response, PartnerTransferResult.class);
}
/**
* 微信支付批次单号查询批次单API
* 接口说明
* 适用对象:服务商
* 请求URLhttps://api.mch.weixin.qq.com/v3/partner-transfer/batches/batch-id/{batch_id}
* https://api.mch.weixin.qq.com/v3/partner-transfer/batches/batch-id/1030000071100999991182020050700019480001?need_query_detail=true&offset=1
* 请求方式GET
* 接口限频:单个服务商 50QPS如果超过频率限制会报错FREQUENCY_LIMITED请降低频率请求。
*
* @param request 请求对象
* @return 返回数据 {@link BatchNumberResult}
* @throws WxPayException the wx pay exception
*/
@Override
public BatchNumberResult queryBatchByBatchId(BatchNumberRequest request) throws WxPayException {
String url = String.format("%s/v3/partner-transfer/batches/batch-id/%s", this.payService.getPayBaseUrl(), request.getBatchId());
if (request.getOffset() == null) {
request.setOffset(0);
}
if (request.getLimit() == null || request.getLimit() <= 0) {
request.setLimit(20);
}
String query = String.format("?need_query_detail=true&detail_status=ALL&offset=%s&limit=%s", request.getNeedQueryDetail(), request.getOffset(), request.getLimit());
if (StringUtil.isNotBlank(request.getDetailStatus())){
query += "&detail_status="+request.getDetailStatus();
}
String response = this.payService.getV3(url + query);
return GSON.fromJson(response, BatchNumberResult.class);
}
/**
* 商家批次单号查询批次单API
* 接口说明
* 适用对象:服务商
* 请求URLhttps://api.mch.weixin.qq.com/v3/partner-transfer/batches/out-batch-no/{out_batch_no}
* 请求方式GET
* 接口限频:单个服务商 50QPS如果超过频率限制会报错FREQUENCY_LIMITED请降低频率请求。
*
* @param request 请求对象
* @return 返回数据 {@link BatchNumberResult}
* @throws WxPayException the wx pay exception
*/
@Override
public BatchNumberResult queryBatchByOutBatchNo(MerchantBatchRequest request) throws WxPayException {
String url = String.format("%s/v3/partner-transfer/batches/out-batch-no/%s", this.payService.getPayBaseUrl(), request.getOutBatchNo());
if (request.getOffset() == null) {
request.setOffset(0);
}
if (request.getLimit() == null || request.getLimit() <= 0) {
request.setLimit(20);
}
String query = String.format("?need_query_detail=true&offset=%s&limit=%s", request.getNeedQueryDetail(), request.getOffset(), request.getLimit());
if (StringUtil.isNotBlank(request.getDetailStatus())){
query += "&detail_status="+request.getDetailStatus();
}
String response = this.payService.getV3(url + query);
return GSON.fromJson(response, BatchNumberResult.class);
}
/**
* 微信支付明细单号查询明细单API
* 接口说明
* 适用对象:服务商
* 请求URLhttps://api.mch.weixin.qq.com/v3/partner-transfer/batches/batch-id/{batch_id}/details/detail-id/{detail_id}
* 请求方式GET
* 接口限频:单个服务商 50QPS如果超过频率限制会报错FREQUENCY_LIMITED请降低频率请求。
*
* @param batchId 微信批次单号
* @param detailId 微信明细单号
* @return 返回数据 {@link BatchDetailsResult}
* @throws WxPayException the wx pay exception
* @throws BadPaddingException the wx decrypt exception
*/
@Override
public BatchDetailsResult queryBatchDetailByWeChat(String batchId, String detailId) throws WxPayException, BadPaddingException {
String url = String.format("%s/v3/partner-transfer/batches/batch-id/%s/details/detail-id/%s", this.payService.getPayBaseUrl(), batchId, detailId);
String response = this.payService.getV3(url);
BatchDetailsResult batchDetailsResult = GSON.fromJson(response, BatchDetailsResult.class);
String userName = RsaCryptoUtil.decryptOAEP(batchDetailsResult.getUserName(), this.payService.getConfig().getPrivateKey());
batchDetailsResult.setUserName(userName);
return batchDetailsResult;
}
/**
* 商家明细单号查询明细单API
* 接口说明
* 适用对象:服务商
* 请求URLhttps://api.mch.weixin.qq.com/v3/partner-transfer/batches/out-batch-no/{out_batch_no}/details/out-detail-no/{out_detail_no}
* 请求方式GET
* 接口限频:单个服务商 50QPS如果超过频率限制会报错FREQUENCY_LIMITED请降低频率请求。
*
* @param outBatchNo 商家明细单号
* @param outDetailNo 商家批次单号
* @return 返回数据 {@link BatchDetailsResult}
* @throws WxPayException the wx pay exception
* @throws BadPaddingException the wx decrypt exception
*/
@Override
public BatchDetailsResult queryBatchDetailByMch(String outBatchNo, String outDetailNo) throws WxPayException, BadPaddingException {
String url = String.format("%s/v3/partner-transfer/batches/out-batch-no/%s/details/out-detail-no/%s", this.payService.getPayBaseUrl(), outBatchNo, outDetailNo);
String response = this.payService.getV3(url);
BatchDetailsResult batchDetailsResult = GSON.fromJson(response, BatchDetailsResult.class);
String userName = RsaCryptoUtil.decryptOAEP(batchDetailsResult.getUserName(), this.payService.getConfig().getPrivateKey());
batchDetailsResult.setUserName(userName);
return batchDetailsResult;
}
/**
* 转账电子回单申请受理API
* 接口说明
* 适用对象:直连商户 服务商
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter4_1.shtml
* 请求URLhttps://api.mch.weixin.qq.com/v3/transfer/bill-receipt
* 请求方式POST
*
* @param request 商家批次单号
* @return 返回数据 fund balance result
* @throws WxPayException the wx pay exception
*/
@Override
public BillReceiptResult receiptBill(ReceiptBillRequest request) throws WxPayException {
String url = String.format("%s/v3/transfer/bill-receipt", this.payService.getPayBaseUrl());
String response = this.payService.postV3(url, GSON.toJson(request));
return GSON.fromJson(response, BillReceiptResult.class);
}
/**
* 查询转账电子回单API
* 接口说明
* 适用对象:直连商户 服务商
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter4_2.shtml
* 请求URLhttps://api.mch.weixin.qq.com/v3/transfer/bill-receipt/{out_batch_no}
* 请求方式GET
*
* @param outBatchNo 商家批次单号
* @return 返回数据 fund balance result
* @throws WxPayException the wx pay exception
*/
@Override
public BillReceiptResult queryBillReceipt(String outBatchNo) throws WxPayException {
String url = String.format("%s/v3/transfer/bill-receipt/%s", this.payService.getPayBaseUrl(), outBatchNo);
String response = this.payService.getV3(url);
return GSON.fromJson(response, BillReceiptResult.class);
}
/**
* 转账明细电子回单受理API
* 接口说明
* 适用对象:直连商户 服务商
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter4_4.shtml
* 请求URLhttps://api.mch.weixin.qq.com/v3/transfer-detail/electronic-receipts
* 请求方式POST
* 前置条件只支持受理最近90天内的转账明细单
*
* @param request 请求对象
* @return 返回数据 fund balance result
* @throws WxPayException the wx pay exception
*/
@Override
public ElectronicReceiptsResult transferElectronic(ElectronicReceiptsRequest request) throws WxPayException {
String url = String.format("%s/v3/transfer-detail/electronic-receipts", this.payService.getPayBaseUrl());
String response = this.payService.postV3(url, GSON.toJson(request));
return GSON.fromJson(response, ElectronicReceiptsResult.class);
}
/**
* 查询转账明细电子回单受理结果API
* 接口说明
* 适用对象:直连商户 服务商
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter4_5.shtml
* 请求URLhttps://api.mch.weixin.qq.com/v3/transfer-detail/electronic-receipts
* 请求方式GET
* 前置条件只支持查询最近90天内的转账明细单
*
* @param request 请求对象
* @return 返回数据 fund balance result
* @throws WxPayException the wx pay exception
*/
@Override
public ElectronicReceiptsResult queryTransferElectronicResult(ElectronicReceiptsRequest request) throws WxPayException {
String url = String.format("%s/v3/transfer-detail/electronic-receipts", this.payService.getPayBaseUrl());
String query = String.format("?accept_type=%s&out_batch_no=%s&out_detail_no=%s", request.getAcceptType(), request.getOutBatchNo(), request.getOutDetailNo());
String response = this.payService.getV3(url + query);
return GSON.fromJson(response, ElectronicReceiptsResult.class);
}
/**
* 下载电子回单API
* 接口说明
* 适用对象:直连商户 服务商
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter4_3.shtml
* 请求URL通过申请账单接口获取到“download_url”URL有效期10min
* 请求方式GET
* 前置条件调用申请账单接口并获取到“download_url”
*
* @param url 微信返回的电子回单地址。
* @return 返回数据 fund balance result
* @throws WxPayException the wx pay exception
*/
@Override
public InputStream transferDownload(String url) throws WxPayException {
InputStream response = this.payService.downloadV3(url);
return response;
}
/**
* <pre>
* 查询账户实时余额API
* 接口说明
* 适用对象:直连商户 服务商
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter5_1.shtml
* 请求URLhttps://api.mch.weixin.qq.com/v3/merchant/fund/balance/{account_type}
* 请求方式GET
* </pre>
*
* @param accountType 服务商账户类型 {@link SpAccountTypeEnum}
* @return 返回数据 fund balance result
* @throws WxPayException the wx pay exception
*/
@Override
public FundBalanceResult fundBalance(SpAccountTypeEnum accountType) throws WxPayException {
String url = String.format("%s/v3/merchant/fund/balance/%s", this.payService.getPayBaseUrl(), accountType);
String response = this.payService.getV3(url);
return GSON.fromJson(response, FundBalanceResult.class);
}
/**
* <pre>
* 服务商账户日终余额
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter5_2.shtml
* 文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pages/amount.shtml
* </pre>
*
* @param accountType 服务商账户类型
* @param date 查询日期 2020-09-11
* @return 返回数据 fund balance result
* @throws WxPayException the wx pay exception
*/
@Override
public FundBalanceResult spDayEndBalance(SpAccountTypeEnum accountType, String date) {
try {
return this.payService.getEcommerceService().spDayEndBalance(accountType, date);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

View File

@@ -0,0 +1,192 @@
package com.github.binarywang.wxpay.service.impl;
import com.github.binarywang.wxpay.bean.marketing.payroll.*;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.PayrollService;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.v3.util.RsaCryptoUtil;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import javax.crypto.IllegalBlockSizeException;
/**
* 微信支付-微工卡
*
* @author xiaoqiang
* @date 2021/12/2
*/
@Slf4j
@RequiredArgsConstructor
public class PayrollServiceImpl implements PayrollService {
private static final Gson GSON = new GsonBuilder().create();
private final WxPayService payService;
/**
* 生成授权token
* 适用对象:服务商
* 请求URLhttps://api.mch.weixin.qq.com/v3/payroll-card/tokens
* 请求方式POST
*
* @param request 请求参数
* @return 返回数据
* @throws WxPayException the wx pay exception
*/
@Override
public TokensResult payrollCardTokens(TokensRequest request) throws WxPayException {
String url = String.format("%s/v3/payroll-card/tokens", payService.getPayBaseUrl());
try {
String userName = RsaCryptoUtil.encryptOAEP(request.getUserName(), payService.getConfig().getVerifier().getValidCertificate());
request.setUserName(userName);
String idCardNumber = RsaCryptoUtil.encryptOAEP(request.getIdCardNumber(), payService.getConfig().getVerifier().getValidCertificate());
request.setIdCardNumber(idCardNumber);
} catch (IllegalBlockSizeException e) {
throw new RuntimeException("加密异常!", e);
}
String response = payService.postV3(url, GSON.toJson(request));
return GSON.fromJson(response, TokensResult.class);
}
/**
* 查询微工卡授权关系API
* 适用对象:服务商
* 请求URLhttps://api.mch.weixin.qq.com/v3/payroll-card/relations/{openid}
* 请求方式GET
*
* @param request 请求参数
* @return 返回数据
* @throws WxPayException the wx pay exception
*/
@Override
public RelationsResult payrollCardRelations(RelationsRequest request) throws WxPayException {
String url = String.format("%s/v3/payroll-card/relations/%s",
payService.getPayBaseUrl(), request.getOpenid());
String query = String.format("?sub_mchid=%s", request.getSubMchid());
if (StringUtils.isNotEmpty(request.getAppid())) {
query += "&appid=" + request.getAppid();
}
if (StringUtils.isNotEmpty(request.getSubAppid())) {
query += "&sub_appid=" + request.getSubAppid();
}
String response = payService.getV3(url + query);
return GSON.fromJson(response, RelationsResult.class);
}
/**
* 微工卡核身预下单API
* 适用对象:服务商
* 请求URLhttps://api.mch.weixin.qq.com/v3/payroll-card/authentications/pre-order
* 请求方式POST
*
* @param request 请求参数
* @return 返回数据
* @throws WxPayException the wx pay exception
*/
@Override
public PreOrderResult payrollCardPreOrder(PreOrderRequest request) throws WxPayException {
String url = String.format("%s/v3/payroll-card/authentications/pre-order", payService.getPayBaseUrl());
String response = payService.postV3(url, GSON.toJson(request));
return GSON.fromJson(response, PreOrderResult.class);
}
/**
* 获取核身结果API
* 适用对象:服务商
* 请求URLhttps://api.mch.weixin.qq.com/v3/payroll-card/authentications/{authenticate_number}
* 请求方式GET
*
* @param subMchid 子商户号
* @param authenticateNumber 商家核身单号
* @return 返回数据
* @throws WxPayException the wx pay exception
*/
@Override
public AuthenticationsResult payrollCardAuthenticationsNumber(String subMchid, String authenticateNumber) throws WxPayException {
String url = String.format("%s/v3/payroll-card/authentications/%s", payService.getPayBaseUrl(), authenticateNumber);
String query = String.format("?sub_mchid=%s", subMchid);
String response = payService.getV3(url + query);
return GSON.fromJson(response, AuthenticationsResult.class);
}
/**
* 查询核身记录API
* 适用对象:服务商
* 请求URLhttps://api.mch.weixin.qq.com/v3/payroll-card/authentications
* 请求方式GET
*
* @param request 请求参数
* @return 返回数据
* @throws WxPayException the wx pay exception
*/
@Override
public AuthRecordResult payrollCardAuthentications(AuthRecordRequest request) throws WxPayException {
String url = String.format("%s/v3/payroll-card/authentications", payService.getPayBaseUrl());
String query = String.format("?openid=%s&sub_mchid=%s&authenticate_date=%s",
request.getOpenid(), request.getAppid(), request.getSubMchid(), request.getAuthenticateDate());
if (StringUtils.isNotEmpty(request.getAppid())) {
query += "&appid=" + request.getAppid();
}
if (StringUtils.isNotEmpty(request.getAppid())) {
query += "&sub_appid=" + request.getSubAppid();
}
if (StringUtils.isNotEmpty(request.getAuthenticateState())) {
query += "&authenticate_state=" + request.getAuthenticateState();
}
String response = payService.getV3(url + query);
return GSON.fromJson(response, AuthRecordResult.class);
}
/**
* 微工卡核身预下单(流程中完成授权)
* 适用对象:服务商
* 请求URLhttps://api.mch.weixin.qq.com/v3/payroll-card/authentications/pre-order-with-auth
* 请求方式POST
*
* @param request 请求参数
* @return 返回数据
* @throws WxPayException the wx pay exception
*/
@Override
public PreOrderWithAuthResult payrollCardPreOrderWithAuth(PreOrderWithAuthRequest request) throws WxPayException {
String url = String.format("%s/v3/payroll-card/authentications/pre-order-with-auth", payService.getPayBaseUrl());
try {
String userName = RsaCryptoUtil.encryptOAEP(request.getUserName(), payService.getConfig().getVerifier().getValidCertificate());
request.setUserName(userName);
String idCardNumber = RsaCryptoUtil.encryptOAEP(request.getIdCardNumber(), payService.getConfig().getVerifier().getValidCertificate());
request.setIdCardNumber(idCardNumber);
} catch (IllegalBlockSizeException e) {
throw new RuntimeException("敏感信息加密异常!", e);
}
String response = payService.postV3(url, GSON.toJson(request));
return GSON.fromJson(response, PreOrderWithAuthResult.class);
}
/**
* 按日下载提现异常文件API
* 适用对象:服务商
* 请求URLhttps://api.mch.weixin.qq.com/v3/merchant/fund/withdraw/bill-type/{bill_type}
* 请求方式GET
*
* @param billType 账单类型
* NO_SUCC提现异常账单包括提现失败和提现退票账单。
* 示例值NO_SUCC
* @param billDate 账单日期 表示所在日期的提现账单格式为YYYY-MM-DD。
* 例如2008-01-01日发起的提现2008-01-03日银行返回提现失败则该提现数据将出现在bill_date为2008-01-03日的账单中。
* 示例值2019-08-17
* @return 返回数据
* @throws WxPayException the wx pay exception
*/
@Override
public PreOrderWithAuthResult merchantFundWithdrawBillType(String billType, String billDate) throws WxPayException {
String url = String.format("%s/v3/merchant/fund/withdraw/bill-type/%s", payService.getPayBaseUrl(), billType);
String query = String.format("?bill_date=%s", billDate);
String response = payService.getV3(url + query);
return GSON.fromJson(response, PreOrderWithAuthResult.class);
}
}