查询用户授权

创建、查询、取消微信支付分订单
This commit is contained in:
xiafang
2020-12-15 16:28:00 +08:00
parent dff0bca100
commit a71de66dad
15 changed files with 515 additions and 50 deletions

View File

@@ -4,7 +4,9 @@
- [Java中的微信支付2API V3 微信平台证书的获取与刷新](https://mp.weixin.qq.com/s/O_YcnIRcl2MltElBupm3Hg)
- [Java中的微信支付3API V3对微信服务器响应进行签名验证](https://mp.weixin.qq.com/s/cb2eTTRjHifNYUGpQETMCQ)
### 微信支付V3中的坑
#### 代金券
- 代金券制券后不能修改,所以一定要注意
- 已激活的代金券批次被停用后不影响该批次已发放代金券的核销
- 代金券激活券和制券要有一定的间隔时间官方说是1分钟
- 发券不需要靠微信服务号官方的描述是错误的有一个支持微信登录的appid就行了
- 制券 返回`403`,报文`{"code":"REQUEST_BLOCKED","message":"活动未开始或已结束\n"}`检查规则是否符合:
@@ -20,6 +22,14 @@
7.out_request_no校验规则不可以重复
8.活动时间不可以大于90天
```
#### 微信支付分
- 微信支付分`service_id`相关
```
1. 在微信支付分功能申请成功后,联系运营那边配置 service_id。
2. service_id 是微信支付商户属性
3. 一个 service_id 可以对应多个 mchid
4. 更多的就要联系微信支付BD了
```
!> 不要过分相信微信文档,微信文档不一定是真的,要问就问他们客服

View File

@@ -114,7 +114,27 @@ public enum WechatPayV3Type {
*
* @since 1.0.2.RELEASE
*/
PAY_SCORE_USER_SERVICE_STATE(HttpMethod.GET,"%s/v3/payscore/user-service-state?service_id={service_id}&appid={appid}&openid={openid}"),
PAY_SCORE_USER_SERVICE_STATE(HttpMethod.GET, "%s/v3/payscore/user-service-state?service_id={service_id}&appid={appid}&openid={openid}"),
/**
* 创建支付分订单API
*
* @since 1.0.2.RELEASE
*/
PAY_SCORE_CREATE_USER_SERVICE_ORDER(HttpMethod.POST, "%s/v3/payscore/serviceorder"),
/**
* 查询支付分订单API
*
* @since 1.0.2.RELEASE
*/
PAY_SCORE_QUERY_USER_SERVICE_ORDER(HttpMethod.GET, "%s/v3/payscore/serviceorder"),
/**
* 取消支付分订单API
*
* @since 1.0.2.RELEASE
*/
PAY_SCORE_CANCEL_USER_SERVICE_ORDER(HttpMethod.POST, "%s/v3/payscore/serviceorder/{out_order_no}/cancel"),
/**
* 创建代金券批次API.
*

View File

@@ -26,6 +26,7 @@ public class WechatApiProvider {
*
* @param tenantId the tenant id
* @return the wechat marketing favor api
* @since 1.0.0.RELEASE
*/
public WechatMarketingFavorApi favorApi(String tenantId) {
return new WechatMarketingFavorApi(this.wechatPayClient, tenantId);
@@ -36,6 +37,7 @@ public class WechatApiProvider {
*
* @param tenantId the tenant id
* @return the wechat pay api
* @since 1.0.0.RELEASE
*/
public WechatDirectPayApi directPayApi(String tenantId) {
return new WechatDirectPayApi(wechatPayClient, tenantId);
@@ -46,16 +48,31 @@ public class WechatApiProvider {
*
* @param tenantId the tenant id
* @return the wechat combine pay api
* @since 1.0.1.RELEASE
*/
public WechatCombinePayApi combinePayApi(String tenantId) {
return new WechatCombinePayApi(wechatPayClient, tenantId);
}
/**
* 微信支付分.
*
* @param tenantId the tenant id
* @return the wechat pay score api
* @since 1.0.2.RELEASE
*/
public WechatPayScoreApi payScoreApi(String tenantId) {
return new WechatPayScoreApi(wechatPayClient, tenantId);
}
/**
* 回调.
*
* 需要处理白名单、幂等性问题。
*
* @param tenantId the tenant id
* @return the wechat pay callback
* @since 1.0.0.RELEASE
*/
public WechatPayCallback callback(String tenantId) {
return new WechatPayCallback(wechatPayClient.signatureProvider(), tenantId);

View File

@@ -71,12 +71,13 @@ public class WechatMarketingFavorApi extends AbstractApi {
* @return the request entity
*/
private RequestEntity<?> createStocksFunction(WechatPayV3Type type, StocksCreateParams params) {
WechatPayProperties.V3 v3 = this.wechatMetaBean().getV3();
String mchId = v3.getMchId();
URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA))
.build()
.toUri();
WechatPayProperties.V3 v3 = this.wechatMetaBean().getV3();
String mchId = v3.getMchId();
params.setBelongMerchant(mchId);
return Post(uri, params);
}
@@ -220,6 +221,7 @@ public class WechatMarketingFavorApi extends AbstractApi {
return wechatResponseEntity;
}
/**
* Query stocks function request entity.
*
@@ -374,7 +376,7 @@ public class WechatMarketingFavorApi extends AbstractApi {
WechatPayProperties.V3 v3 = this.wechatMetaBean().getV3();
queryParams.add("stock_creator_mchid", v3.getMchId());
String stockId = params.getStockId();
URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA))
URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA))
.queryParams(queryParams)
.build()
.expand(stockId)
@@ -552,7 +554,7 @@ public class WechatMarketingFavorApi extends AbstractApi {
Map<String, Object> meta = new LinkedHashMap<>(2);
String originalFilename = file.getOriginalFilename();
String filename = StringUtils.hasText(originalFilename)? originalFilename :file.getName();
String filename = StringUtils.hasText(originalFilename) ? originalFilename : file.getName();
meta.put("filename", filename);
byte[] digest = SHA256.Digest.getInstance("SHA-256").digest(file.getBytes());

View File

@@ -1,12 +1,27 @@
package cn.felord.payment.wechat.v3;
import cn.felord.payment.wechat.WechatPayProperties;
import cn.felord.payment.wechat.enumeration.WeChatServer;
import cn.felord.payment.wechat.enumeration.WechatPayV3Type;
import cn.felord.payment.wechat.v3.model.payscore.CancelServiceOrderParams;
import cn.felord.payment.wechat.v3.model.payscore.QueryServiceOrderParams;
import cn.felord.payment.wechat.v3.model.payscore.UserPayScoreOrderParams;
import cn.felord.payment.wechat.v3.model.payscore.UserServiceStateParams;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.util.UriComponentsBuilder;
import java.net.URI;
/**
* 微信支付分API.
*
* @author felord.cn
* @since 1.0.2.RELEASE
*/
public class WechatPayScoreApi extends AbstractApi{
public class WechatPayScoreApi extends AbstractApi {
/**
* Instantiates a new Abstract api.
*
@@ -17,4 +32,135 @@ public class WechatPayScoreApi extends AbstractApi{
super(wechatPayClient, tenantId);
}
/**
* 微信支付分-查询用户授权状态API.
* <p>
* 免确认订单起始接口,【免确认订单模式】是高级接口权限,参见:<a href="https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore/chapter2_5.shtml">业务流程说明</a>
* <p>
* 用户申请使用服务时,商户可通过此接口查询用户是否“已授权”本服务。在“已授权”状态下的服务,用户才可以申请使用。
*
* @param params the params
* @return the wechat response entity
*/
public WechatResponseEntity<ObjectNode> userServiceState(UserServiceStateParams params) {
WechatResponseEntity<ObjectNode> wechatResponseEntity = new WechatResponseEntity<>();
this.client().withType(WechatPayV3Type.PAY_SCORE_USER_SERVICE_STATE, params)
.function((wechatPayV3Type, userServiceStateParams) -> {
WechatPayProperties.V3 v3 = this.wechatMetaBean().getV3();
MultiValueMap<String, String> expandParams = new LinkedMultiValueMap<>();
expandParams.add("appid", v3.getAppId());
expandParams.add("service_id", params.getServiceId());
expandParams.add("openid", params.getOpenId());
URI uri = UriComponentsBuilder.fromHttpUrl(wechatPayV3Type.uri(WeChatServer.CHINA))
.build()
.expand(expandParams)
.toUri();
return Get(uri);
})
.consumer(wechatResponseEntity::convert)
.request();
return wechatResponseEntity;
}
/**
* 创建支付分订单API
* <p>
* 用户申请使用服务时,商户可通过此接口申请创建微信支付分订单。
*
* @param params the params
* @return the wechat response entity
*/
public WechatResponseEntity<ObjectNode> createServiceOrder(UserPayScoreOrderParams params) {
WechatResponseEntity<ObjectNode> wechatResponseEntity = new WechatResponseEntity<>();
this.client().withType(WechatPayV3Type.PAY_SCORE_CREATE_USER_SERVICE_ORDER, params)
.function((wechatPayV3Type, orderParams) -> {
WechatPayProperties.V3 v3 = this.wechatMetaBean().getV3();
orderParams.setAppid(v3.getAppId());
orderParams.setNotifyUrl(v3.getDomain().concat(orderParams.getNotifyUrl()));
URI uri = UriComponentsBuilder.fromHttpUrl(wechatPayV3Type.uri(WeChatServer.CHINA))
.build()
.toUri();
return Post(uri, orderParams);
})
.consumer(wechatResponseEntity::convert)
.request();
return wechatResponseEntity;
}
/**
* 查询支付分订单API
* <p>
* 用于查询单笔微信支付分订单详细信息。
*
* @param params the params
* @return the wechat response entity
*/
public WechatResponseEntity<ObjectNode> queryServiceOrder(QueryServiceOrderParams params) {
WechatResponseEntity<ObjectNode> wechatResponseEntity = new WechatResponseEntity<>();
this.client().withType(WechatPayV3Type.PAY_SCORE_QUERY_USER_SERVICE_ORDER, params)
.function((wechatPayV3Type, orderParams) -> {
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
String outOrderNo = orderParams.getOutOrderNo();
if (StringUtils.hasText(outOrderNo)) {
queryParams.add("out_order_no", outOrderNo);
}
String queryId = orderParams.getQueryId();
if (StringUtils.hasText(queryId)) {
queryParams.add("query_id", queryId);
}
queryParams.add("service_id", orderParams.getServiceId());
WechatPayProperties.V3 v3 = this.wechatMetaBean().getV3();
queryParams.add("appid", v3.getAppId());
URI uri = UriComponentsBuilder.fromHttpUrl(wechatPayV3Type.uri(WeChatServer.CHINA))
.queryParams(queryParams)
.build()
.toUri();
return Get(uri);
})
.consumer(wechatResponseEntity::convert)
.request();
return wechatResponseEntity;
}
/**
* 取消支付分订单API
* <p>
* 微信支付分订单创建之后,由于某些原因导致订单不能正常支付时,可使用此接口取消订单。
* <p>
* 订单为以下状态时可以取消订单CREATED已创单、DOING进行中包括商户完结支付分订单后且支付分订单收款状态为待支付USER_PAYING)
* <p>
* 注意:
* • DOING状态包含了订单用户确认、已完结-待支付USER_PAYING的状态因此这种状态下订单也是可以被取消的请确认当前操作是否正确防止误操作将完结后需要支付分收款的单据取消。
*
* @param params the params
* @return the wechat response entity
*/
public WechatResponseEntity<ObjectNode> cancelServiceOrder(CancelServiceOrderParams params) {
WechatResponseEntity<ObjectNode> wechatResponseEntity = new WechatResponseEntity<>();
this.client().withType(WechatPayV3Type.PAY_SCORE_CANCEL_USER_SERVICE_ORDER, params)
.function((wechatPayV3Type, orderParams) -> {
URI uri = UriComponentsBuilder.fromHttpUrl(wechatPayV3Type.uri(WeChatServer.CHINA))
.build()
.expand(orderParams.getOutOrderNo())
.toUri();
orderParams.setOutOrderNo(null);
WechatPayProperties.V3 v3 = this.wechatMetaBean().getV3();
orderParams.setAppid(v3.getAppId());
return Post(uri, orderParams);
})
.consumer(wechatResponseEntity::convert)
.request();
return wechatResponseEntity;
}
}

View File

@@ -1,31 +0,0 @@
package cn.felord.payment.wechat.v3.model.payscore;
import lombok.Data;
import java.util.List;
/**
* 微信支付回调请求参数.
*
* @author felord.cn
* @since 1.0.2.RELEASE
*/
@Data
public class A {
private String appid;
private String attach;
private Location location;
private Boolean needUserConfirm;
private String notifyUrl;
private String openid;
private String outOrderNo;
private List<PostDiscount> postDiscounts;
private List<PostPayment> postPayments;
private RiskFund riskFund;
private String serviceId;
private String serviceIntroduction;
private TimeRange timeRange;
}

View File

@@ -0,0 +1,29 @@
package cn.felord.payment.wechat.v3.model.payscore;
import lombok.Data;
/**
* 取消支付分订单请求参数
*
* @author felord.cn
* @since 1.0.2.RELEASE
*/
@Data
public class CancelServiceOrderParams {
/**
* 商户服务订单号,必填
*/
private String outOrderNo;
/**
* 与传入的商户号建立了支付绑定关系的appid必填
*/
private String appid;
/**
* 服务ID必填
*/
private String serviceId;
/**
* 取消原因最长50个字符必填
*/
private String reason;
}

View File

@@ -4,13 +4,31 @@ package cn.felord.payment.wechat.v3.model.payscore;
import lombok.Data;
/**
* 微信支付回调请求参数.
* 服务位置信息
* <p>
* 如果传入,用户侧则显示此参数。
*
* @author felord.cn
* @since 1.0.2.RELEASE
*/
@Data
public class Location {
private String endLocation;
/**
* 服务开始地点,选填。
* <p>
* 开始使用服务的地点不超过50个字符超出报错处理。
* 【建议】
* 1、用户下单时【未确定】服务结束地点不填写。
* 2、服务在同一地点开始和结束不填写。
* 3、用户下单时【已确定】服务结束地点填写。
*/
private String startLocation;
/**
* 预计服务结束地点,有开始地点时为必填。
*
* 1、结束使用服务的地点不超过50个字符超出报错处理 。
* 2、填写了服务开始地点才能填写服务结束地点。
*/
private String endLocation;
}

View File

@@ -4,7 +4,9 @@ package cn.felord.payment.wechat.v3.model.payscore;
import lombok.Data;
/**
* 微信支付回调请求参数.
* 后付费商户优惠,选填
* <p>
* 最多包含30条商户优惠。如果传入用户侧则显示此参数。
*
* @author felord.cn
* @since 1.0.2.RELEASE
@@ -12,6 +14,23 @@ import lombok.Data;
@Data
public class PostDiscount {
private String description;
/**
* 优惠名称,条件选填
*
* 优惠名称说明name和description若填写则必须同时填写优惠名称不可重复描述。
*/
private String name;
/**
* 优惠说明,条件选填
*
* 优惠使用条件说明。{@link PostDiscount#name}若填写,则必须同时填写。
*/
private String description;
/**
* 优惠数量,选填。
* <p>
* 优惠的数量。
* 特殊规则数量限制100不填时默认1。
*/
private Long count = 1L;
}

View File

@@ -4,17 +4,39 @@ package cn.felord.payment.wechat.v3.model.payscore;
import lombok.Data;
/**
* 微信支付回调请求参数.
* 后付费项目,选填
* <p>
* 最多包含100条付费项目。如果传入用户侧则显示此参数。
*
* @author felord.cn
* @since 1.0.2.RELEASE
*/
@Data
public class PostPayment {
private Long amount;
private Long count;
private String description;
/**
* 付费项目名称,选填。
* <p>
* 相同订单号下不能出现相同的付费项目名称当参数长度超过20个字符时报错处理。
*/
private String name;
/**
* 金额,条件选填。
* <p>
* 此付费项目总金额大于等于0单位为分等于0时代表不需要扣费只能为整数详见支付金额。如果填写了“付费项目名称”则amount或description必须填写其一或都填。
*/
private Long amount;
/**
* 计费说明,条件选填。
* <p>
* 描述计费规则不超过30个字符超出报错处理。如果填写了“付费项目名称”则amount或description必须填写其一或都填。
*/
private String description;
/**
* 付费数量,选填。
* <p>
* 付费项目的数量。
* 特殊规则数量限制100不填时默认1。
*/
private Long count = 1L;
}

View File

@@ -0,0 +1,25 @@
package cn.felord.payment.wechat.v3.model.payscore;
import lombok.Data;
/**
* 查询支付分订单请求参数
*
* @author felord.cn
* @since 1.0.2.RELEASE
*/
@Data
public class QueryServiceOrderParams {
/**
* 商户服务订单号,同{@link QueryServiceOrderParams#queryId} 二选一,而且不能同时为{@link null}
*/
private String outOrderNo;
/**
* 回跳查询ID同{@link QueryServiceOrderParams#outOrderNo} 二选一,而且不能同时为{@link null}
*/
private String queryId;
/**
* 服务ID必填
*/
private String serviceId;
}

View File

@@ -4,7 +4,7 @@ package cn.felord.payment.wechat.v3.model.payscore;
import lombok.Data;
/**
* 微信支付回调请求参数.
* 订单风险金信息,必填
*
* @author felord.cn
* @since 1.0.2.RELEASE
@@ -12,7 +12,50 @@ import lombok.Data;
@Data
public class RiskFund {
/**
* 风险金名称,必填
*/
private Type name;
/**
* 风险金额,必填
* <p>
* 1、数字必须>0单位分
* 2、风险金额≤每个服务ID的风险金额上限。
* 3、当商户优惠字段为空时付费项目总金额≤服务ID的风险金额上限 未填写金额的付费项目视为该付费项目金额为0
* 4、完结金额可大于、小于或等于风险金额。详细可见QA <a src = "https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore/chapter11_2.shtml#menu1">关于订单风险金额问题</a>
*/
private Long amount;
/**
* 风险说明,选填
* <p>
* 文字不超过30个字。
*/
private String description;
private String name;
/**
* 风险金类型
*/
enum Type {
/**
* 押金
*/
DEPOSIT,
/**
* 预付款
*/
ADVANCE,
/**
* 保证金
*/
CASH_DEPOSIT,
/**
* 预估订单费用
* <p>
* 【先享模式】(评估不通过不可使用服务)
*/
ESTIMATE_ORDER_COST
}
}

View File

@@ -4,13 +4,49 @@ package cn.felord.payment.wechat.v3.model.payscore;
import lombok.Data;
/**
* 微信支付回调请求参数.
* 服务时间段,必填
*
* @author felord.cn
* @since 1.0.2.RELEASE
*/
@Data
public class TimeRange {
private String endTime;
/**
* 服务开始时间
* <p>
* 用户端展示用途。
* 用户下单时确认的服务开始时间(比如用户今天下单,明天开始接受服务,这里指的是明天的服务开始时间)。
* 支持三种格式yyyyMMddHHmmss、yyyyMMdd和 OnAccept
* ● 传入20091225091010表示2009年12月25日9点10分10秒。
* ● 传入20091225默认认为时间为2009年12月25日
* ● 传入OnAccept表示用户确认订单成功时间为【服务开始时间】。
* 根据传入时间精准度进行校验
* 1若传入时间精准到秒则校验精准到秒【服务开始时间】>【商户调用创建订单接口时间
* 2若传入时间精准到日则校验精准到日【服务开始时间】>=【商户调用创建订单接口时间】
*/
private String startTime;
/**
* 服务开始时间备注说明服务开始时间有填时可填写服务开始时间备注不超过20个字符超出报错处理。
*/
private String startTimeRemark;
/**
* 用户端展示用途支持两种格式yyyyMMddHHmmss和yyyyMMdd
* ● 传入20091225091010表示2009年12月25日9点10分10秒。
* ● 传入20091225默认认为时间为2009年12月25日
* 根据传入时间精准度进行校验
* 1、若传入时间精准到秒则校验精准到秒
* 1【预计服务结束时间】>【服务开始时间】
* 2【预计服务结束时间】>【商户调用接口时间+1分钟】
* 2、若传入时间精准到日则校验精准到日
* 1【预计服务结束时间】>=【服务开始时间】
* 2【预计服务结束时间】>=【商户调用接口时间】
* 【建议】
* 1、用户下单时【未确定】服务结束时间不填写。
* 2、用户下单时【已确定】服务结束时间填写。
*/
private String endTime;
/**
* 预计服务结束时间备注说明预计服务结束时间有填时可填写预计服务结束时间备注不超过20个字符超出报错处理。
*/
private String endTimeRemark;
}

View File

@@ -0,0 +1,85 @@
package cn.felord.payment.wechat.v3.model.payscore;
import lombok.Data;
import java.util.List;
/**
* 创建支付分订单请求参数.
*
* @author felord.cn
* @since 1.0.2.RELEASE
*/
@Data
public class UserPayScoreOrderParams {
/**
* 商户服务订单号,必填
* <p>
* 商户系统内部服务订单号不是交易单号要求此参数只能由数字、大小写字母_-|*组成,且在同一个商户号下唯一。详见[商户订单号]。
*/
private String outOrderNo;
/**
* 与传入的商户号建立了支付绑定关系的appid必填
*/
private String appid;
/**
* 服务ID必填
* <p>
* 该服务ID有本接口对应产品的权限。
*/
private String serviceId;
/**
* 服务信息,必填
* <p>
* 用于介绍本订单所提供的服务 当参数长度超过20个字符时报错处理。
*/
private String serviceIntroduction;
/**
* 后付费项目,选填
*/
private List<PostPayment> postPayments;
/**
* 后付费商户优惠,选填
*/
private List<PostDiscount> postDiscounts;
/**
* 服务时间段,必填
*/
private TimeRange timeRange;
/**
* 服务位置,选填
*/
private Location location;
/**
* 订单风险金,必填
*/
private RiskFund riskFund;
/**
* 商户数据包,选填
* <p>
* 商户数据包可存放本订单所需信息需要先urlencode后传入。 当商户数据包总长度超出256字符时报错处理。
*/
private String attach;
/**
* 商户回调地址,必填
*/
private String notifyUrl;
/**
* 微信用户在商户对应appid下的唯一标识条件选填
* <p>
* 免确认订单:必填
* 需确认订单:不填
*/
private String openid;
/**
* 是否需要用户确认,选填
* <p>
* false免确认订单
* true需确认订单
* 默认值true
*/
private Boolean needUserConfirm = Boolean.TRUE;
}

View File

@@ -0,0 +1,24 @@
package cn.felord.payment.wechat.v3.model.payscore;
import lombok.Data;
/**
* 查询用户授权状态参数.
* <p>
* {@code appid} 从对应租户的配置中自动注入。
*
* @author felord.cn
* @since 1.0.2.RELEASE
*/
@Data
public class UserServiceStateParams {
/**
* 微信支付分 服务ID , 需要微信侧运营操作绑定到商户。
*/
private String serviceId;
/**
* 微信用户在商户对应appid下的唯一标识。
*/
private String openId;
}