微信先享卡回调

This commit is contained in:
dax
2020-12-18 16:56:17 +08:00
parent 84a4716d43
commit 54e2c28e57
10 changed files with 470 additions and 145 deletions

View File

@@ -0,0 +1,18 @@
package cn.felord.payment.wechat.enumeration;
/**
* 目标完成类型、优惠使用类型
*
* @author felord.cn
* @since 1.0.2.RELEASE
*/
public enum StrategyType {
/**
* 增加数量,表示用户发生了履约行为
*/
INCREASE,
/**
* 减少数量,表示取消用户的履约行为(例如用户取消购买、退货退款等)
*/
DECREASE
}

View File

@@ -6,7 +6,7 @@ import cn.felord.payment.wechat.v3.model.CouponConsumeData;
import cn.felord.payment.wechat.v3.model.ResponseSignVerifyParams;
import cn.felord.payment.wechat.v3.model.TransactionConsumeData;
import cn.felord.payment.wechat.v3.model.combine.CombineTransactionConsumeData;
import cn.felord.payment.wechat.v3.model.discountcard.DiscountCardAcceptedConsumeData;
import cn.felord.payment.wechat.v3.model.discountcard.*;
import cn.felord.payment.wechat.v3.model.payscore.PayScoreUserConfirmConsumeData;
import cn.felord.payment.wechat.v3.model.payscore.PayScoreUserPaidConsumeData;
import cn.felord.payment.wechat.v3.model.payscore.PayScoreUserPermissionConsumeData;
@@ -27,6 +27,8 @@ import java.util.function.Consumer;
/**
* 微信支付回调工具.
* <p>
* 注意:<strong>开发者应该保证回调调用的幂等性</strong>
* <p>
* 支付通知http应答码为200或204才会当作正常接收当回调处理异常时应答的HTTP状态码应为500或者4xx。
*
* @author felord.cn
@@ -123,40 +125,34 @@ public class WechatPayCallback {
}
/**
* 微信支付分确认订单回调通知.
* 微信支付分确认订单、支付成功回调通知.
* <p>
* 该链接是通过商户[创建支付分订单]提交notify_url参数必须为https协议。如果链接无法访问商户将无法接收到微信通知。 通知url必须为直接可访问的url不能携带参数。示例 “https://pay.weixin.qq.com/wxpay/pay.action”
* 该链接是通过商户 <a target= "_blank" href= "https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/payscore/chapter3_1.shtml">创建支付分订单</a> 提交notify_url参数必须为https协议。如果链接无法访问商户将无法接收到微信通知。 通知url必须为直接可访问的url不能携带参数。示例 “https://pay.weixin.qq.com/wxpay/pay.action”
*
* @param params the params
* @param consumeDataConsumer the consume data consumer
* @param params the params
* @param payScoreConsumer the pay score consumer
* @return the map
* @since 1.0.2.RELEASE
*/
@SneakyThrows
public Map<String, ?> payscoreUserConfirmCallback(ResponseSignVerifyParams params, Consumer<PayScoreUserConfirmConsumeData> consumeDataConsumer) {
String data = this.callback(params, EventType.PAYSCORE_USER_CONFIRM);
PayScoreUserConfirmConsumeData consumeData = MAPPER.readValue(data, PayScoreUserConfirmConsumeData.class);
consumeDataConsumer.accept(consumeData);
return Collections.singletonMap("code", "SUCCESS");
}
public Map<String, ?> payscoreUserOrderCallback(ResponseSignVerifyParams params, PayScoreConsumer payScoreConsumer) {
CallbackParams callbackParams = resolve(params);
String eventType = callbackParams.getEventType();
if (Objects.equals(eventType, EventType.PAYSCORE_USER_CONFIRM.event)) {
String data = this.decrypt(callbackParams);
PayScoreUserConfirmConsumeData confirmConsumeData = MAPPER.readValue(data, PayScoreUserConfirmConsumeData.class);
payScoreConsumer.getConfirmConsumeDataConsumer().accept(confirmConsumeData);
} else if (Objects.equals(eventType, EventType.PAYSCORE_USER_PAID.event)) {
String data = this.decrypt(callbackParams);
PayScoreUserPaidConsumeData paidConsumeData = MAPPER.readValue(data, PayScoreUserPaidConsumeData.class);
payScoreConsumer.getPaidConsumeDataConsumer().accept(paidConsumeData);
} else {
log.error("wechat pay event type is not matched, callbackParams {}", callbackParams);
throw new PayException(" wechat pay event type is not matched");
}
/**
* 微信支付分支付成功回调通知API.
* <p>
* 请求URL该链接是通过商户[创建支付分订单]提交notify_url参数必须为https协议。如果链接无法访问商户将无法接收到微信通知。 通知url必须为直接可访问的url不能携带参数。示例 “https://pay.weixin.qq.com/wxpay/pay.action”
*
* @param params the params
* @param consumeDataConsumer the consume data consumer
* @return the map
* @since 1.0.2.RELEASE
*/
@SneakyThrows
public Map<String, ?> payscoreUserPaidCallback(ResponseSignVerifyParams params, Consumer<PayScoreUserPaidConsumeData> consumeDataConsumer) {
String data = this.callback(params, EventType.PAYSCORE_USER_PAID);
PayScoreUserPaidConsumeData consumeData = MAPPER.readValue(data, PayScoreUserPaidConsumeData.class);
consumeDataConsumer.accept(consumeData);
return Collections.singletonMap("code", "SUCCESS");
}
@@ -192,20 +188,42 @@ public class WechatPayCallback {
}
/**
* Discount card user accepted callback map.
* 用户领卡、守约状态变化、扣费状态变化通知API
* <p>
* 用户领取优惠卡后或者用户守约状态发生变更后或扣费状态变化后,微信会把对应信息发送给商户。
* <p>
* 该链接是通过商户<a target= "_blank" href= "https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/discount-card/chapter3_1.shtml">预受理领卡请求</a>中提交notify_url参数必须为https协议。如果链接无法访问商户将无法接收到微信通知。 通知url必须为直接可访问的url不能携带参数。示例 “https://pay.weixin.qq.com/wxpay/pay.action”
*
* @param params the params
* @param consumeDataConsumer the consume data consumer
* @param params the params
* @param discountCardConsumer the discount card consumer
* @return the map
*/
@SneakyThrows
public Map<String, ?> discountCardUserAcceptedCallback(ResponseSignVerifyParams params, Consumer<DiscountCardAcceptedConsumeData> consumeDataConsumer) {
String data = this.callback(params, EventType.DISCOUNT_CARD_USER_ACCEPTED);
DiscountCardAcceptedConsumeData consumeData = MAPPER.readValue(data, DiscountCardAcceptedConsumeData.class);
consumeDataConsumer.accept(consumeData);
public Map<String, ?> discountCardCallback(ResponseSignVerifyParams params, DiscountCardConsumer discountCardConsumer) {
CallbackParams callbackParams = resolve(params);
String eventType = callbackParams.getEventType();
if (Objects.equals(eventType, EventType.DISCOUNT_CARD_AGREEMENT_ENDED.event)) {
String data = this.decrypt(callbackParams);
DiscountCardAgreementEndConsumeData agreementEndConsumeData = MAPPER.readValue(data, DiscountCardAgreementEndConsumeData.class);
discountCardConsumer.getAgreementEndConsumeDataConsumer().accept(agreementEndConsumeData);
} else if (Objects.equals(eventType, EventType.DISCOUNT_CARD_USER_ACCEPTED.event)) {
String data = this.decrypt(callbackParams);
DiscountCardAcceptedConsumeData acceptedConsumeData = MAPPER.readValue(data, DiscountCardAcceptedConsumeData.class);
discountCardConsumer.getAcceptedConsumeDataConsumer().accept(acceptedConsumeData);
} else if (Objects.equals(eventType, EventType.DISCOUNT_CARD_USER_PAID.event)) {
String data = this.decrypt(callbackParams);
DiscountCardUserPaidConsumeData paidConsumeData = MAPPER.readValue(data, DiscountCardUserPaidConsumeData.class);
discountCardConsumer.getCardUserPaidConsumeDataConsumer().accept(paidConsumeData);
} else {
log.error("wechat pay event type is not matched, callbackParams {}", callbackParams);
throw new PayException(" wechat pay event type is not matched");
}
return Collections.singletonMap("code", "SUCCESS");
}
/**
* Callback.
*
@@ -301,14 +319,14 @@ public class WechatPayCallback {
DISCOUNT_CARD_USER_ACCEPTED("DISCOUNT_CARD.USER_ACCEPTED"),
/**
* TODO 微信先享卡守约状态变化事件.
* 微信先享卡守约状态变化事件.
*
* @since 1.0.2.RELEASE
*/
DISCOUNT_CARD_AGREEMENT_ENDED("DISCOUNT_CARD.AGREEMENT_ENDED"),
/**
* TODO 微信先享卡扣费状态变化事件.
* 微信先享卡扣费状态变化事件.
*
* @since 1.0.2.RELEASE
*/

View File

@@ -181,8 +181,7 @@ public class WechatPayClient {
Assert.notNull(httpMethod, "httpMethod is required");
HttpHeaders headers = requestEntity.getHeaders();
T entityBody = requestEntity.getBody();
String body = requestEntity.hasBody() ? Objects.requireNonNull(entityBody).toString() : "";
String body = requestEntity.hasBody() ? Objects.requireNonNull(requestEntity.getBody()).toString() : "";
if (WechatPayV3Type.MARKETING_IMAGE_UPLOAD.pattern().contains(canonicalUrl)) {
body = Objects.requireNonNull(headers.get("Meta-Info")).get(0);
}
@@ -193,7 +192,7 @@ public class WechatPayClient {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.addAll(headers);
httpHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
// 兼容图片上传,自定义优先级最高
// for upload
if (Objects.isNull(httpHeaders.getContentType())) {
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
}
@@ -218,13 +217,13 @@ public class WechatPayClient {
HttpHeaders headers = responseEntity.getHeaders();
ObjectNode body = responseEntity.getBody();
HttpStatus statusCode = responseEntity.getStatusCode();
// 微信请求id
String requestId = headers.getFirst("Request-ID");
if (!statusCode.is2xxSuccessful()) {
throw new PayException("wechat pay server error,statusCode " + statusCode + ",result : " + body);
throw new PayException("wechat pay server error, Request-ID "+requestId+" , statusCode " + statusCode + ",result : " + body);
}
ResponseSignVerifyParams params = new ResponseSignVerifyParams();
// 微信请求回调id
// String RequestId = response.header("Request-ID");
// 微信平台证书序列号 用来取微信平台证书
params.setWechatpaySerial(headers.getFirst("Wechatpay-Serial"));
//获取应答签名
@@ -244,7 +243,7 @@ public class WechatPayClient {
responseConsumer.accept(responseEntity);
}
} else {
throw new PayException("wechat pay signature failed");
throw new PayException("wechat pay signature failed, Request-ID "+requestId );
}
}
@@ -261,11 +260,13 @@ public class WechatPayClient {
String body = responseEntity.getBody();
HttpStatus statusCode = responseEntity.getStatusCode();
// 微信请求id
String requestId = requestEntity.getHeaders().getFirst("Request-ID");
if (!statusCode.is2xxSuccessful()) {
throw new PayException("wechat pay server error,statusCode " + statusCode + ",result : " + body);
throw new PayException("wechat pay server error, Request-ID "+requestId+" , statusCode " + statusCode + ",result : " + body);
}
if (Objects.isNull(body)) {
throw new PayException("cant obtain wechat response body");
throw new PayException("cant obtain wechat response body, Request-ID "+requestId);
}
return body;
}

View File

@@ -0,0 +1,165 @@
package cn.felord.payment.wechat.v3.model.discountcard;
import lombok.Data;
import java.util.List;
/**
* 微信支付先享卡用户守约状态变化通知解密
*
* @author felord.cn
* @since 1.0.2.RELEASE
*/
@Data
public class DiscountCardAgreementEndConsumeData {
/**
* The Appid.
*/
private String appid;
/**
* The Card id.
*/
private String cardId;
/**
* The Card template id.
*/
private String cardTemplateId;
/**
* The Create time.
*/
private String createTime;
/**
* The Mchid.
*/
private String mchid;
/**
* The Objectives.
*/
private List<Objective> objectives;
/**
* The Openid.
*/
private String openid;
/**
* The Out card code.
*/
private String outCardCode;
/**
* The Rewards.
*/
private List<Reward> rewards;
/**
* The State.
*/
private String state;
/**
* The Time range.
*/
private TimeRange timeRange;
/**
* The Total amount.
*/
private Long totalAmount;
/**
* The Unfinished reason.
*/
private String unfinishedReason;
/**
* The type Objective.
*
* @author felord.cn
* @since 1.0.2.RELEASE
*/
@Data
public static class Objective {
/**
* The Count.
*/
private Long count;
/**
* The Description.
*/
private String description;
/**
* The Name.
*/
private String name;
/**
* The Objective completion records.
*/
private List<ObjectiveCompletionRecord> objectiveCompletionRecords;
/**
* The Objective id.
*/
private String objectiveId;
/**
* The Unit.
*/
private String unit;
}
/**
* The type Time range.
*
* @author felord.cn
* @since 1.0.2.RELEASE
*/
@Data
public static class TimeRange {
/**
* The Betin time.
*/
private String betinTime;
/**
* The End time.
*/
private String endTime;
}
/**
* The type Reward.
*
* @author felord.cn
* @since 1.0.2.RELEASE
*/
@Data
public static class Reward {
/**
* The Amount.
*/
private Long amount;
/**
* The Count.
*/
private Long count;
/**
* The Count type.
*/
private String countType;
/**
* The Description.
*/
private String description;
/**
* The Name.
*/
private String name;
/**
* The Reward id.
*/
private String rewardId;
/**
* The Reward usage records.
*/
private List<RewardUsageRecord> rewardUsageRecords;
/**
* The Unit.
*/
private String unit;
}
}

View File

@@ -0,0 +1,27 @@
package cn.felord.payment.wechat.v3.model.discountcard;
import lombok.Data;
import java.util.function.Consumer;
/**
* 先享卡回调消费复合消费器
*
* @author felord.cn
* @since 1.0.2.RELEASE
*/
@Data
public class DiscountCardConsumer {
/**
* The Accepted consume data consumer.
*/
private Consumer<DiscountCardAcceptedConsumeData> acceptedConsumeDataConsumer;
/**
* The Agreement end consume data consumer.
*/
private Consumer<DiscountCardAgreementEndConsumeData> agreementEndConsumeDataConsumer;
/**
* The Card user paid consume data consumer.
*/
private Consumer<DiscountCardUserPaidConsumeData> cardUserPaidConsumeDataConsumer;
}

View File

@@ -0,0 +1,79 @@
package cn.felord.payment.wechat.v3.model.discountcard;
import lombok.Data;
/**
* 先享卡扣费状态变化通知解密.
*
*
* @author felord.cn
* @since 1.0.2.RELEASE
*/
@Data
public class DiscountCardUserPaidConsumeData {
/**
* The Appid.
*/
private String appid;
/**
* The Card id.
*/
private String cardId;
/**
* The Card template id.
*/
private String cardTemplateId;
/**
* The Mchid.
*/
private String mchid;
/**
* The Openid.
*/
private String openid;
/**
* The Out card code.
*/
private String outCardCode;
/**
* The Pay information.
*/
private PayInformation payInformation;
/**
* The State.
*/
private String state;
/**
* The Total amount.
*/
private Long totalAmount;
/**
* The Unfinished reason.
*/
private String unfinishedReason;
/**
* The type Pay information.
*/
@Data
public static class PayInformation {
/**
* The Pay amount.
*/
private Long payAmount;
/**
* The Pay state.
*/
private String payState;
/**
* The Pay time.
*/
private String payTime;
/**
* The Transaction id.
*/
private String transactionId;
}
}

View File

@@ -0,0 +1,44 @@
package cn.felord.payment.wechat.v3.model.discountcard;
import cn.felord.payment.wechat.enumeration.StrategyType;
import lombok.Data;
/**
* The type Objective completion record.
*
* @author felord.cn
* @since 1.0.2.RELEASE
*/
@Data
public class ObjectiveCompletionRecord {
/**
* The Completion count.
*/
private Long completionCount;
/**
* The Completion time.
*/
private String completionTime;
/**
* The Completion type.
*/
private StrategyType completionType;
/**
* The Description.
*/
private String description;
/**
* The Objective completion serial no.
*/
private String objectiveCompletionSerialNo;
/**
* The Objective id.
*/
private String objectiveId;
/**
* The Remark.
*/
private String remark;
}

View File

@@ -0,0 +1,25 @@
package cn.felord.payment.wechat.v3.model.discountcard;
import cn.felord.payment.wechat.v3.model.payscore.PayScoreUserConfirmConsumeData;
import cn.felord.payment.wechat.v3.model.payscore.PayScoreUserPaidConsumeData;
import lombok.Data;
import java.util.function.Consumer;
/**
* 支付分回调复合消费器
*
* @author felord.cn
* @since 1.0.2.RELEASE
*/
@Data
public class PayScoreConsumer {
/**
* The Confirm consume data consumer.
*/
private Consumer<PayScoreUserConfirmConsumeData> confirmConsumeDataConsumer;
/**
* The Paid consume data consumer.
*/
private Consumer<PayScoreUserPaidConsumeData> paidConsumeDataConsumer;
}

View File

@@ -0,0 +1,48 @@
package cn.felord.payment.wechat.v3.model.discountcard;
import cn.felord.payment.wechat.enumeration.StrategyType;
import lombok.Data;
/**
* The type Reward usage record.
*
* @author felord.cn
* @since 1.0.2.RELEASE
*/
@Data
public class RewardUsageRecord {
/**
* The Amount.
*/
private Long amount;
/**
* The Description.
*/
private String description;
/**
* The Remark.
*/
private String remark;
/**
* The Reward id.
*/
private String rewardId;
/**
* The Reward usage serial no.
*/
private String rewardUsageSerialNo;
/**
* The Usage count.
*/
private Long usageCount;
/**
* The Usage time.
*/
private String usageTime;
/**
* The Usage type.
*/
private StrategyType usageType;
}

View File

@@ -31,104 +31,4 @@ public class UserRecordsParams {
*/
private List<RewardUsageRecord> rewardUsageRecords;
/**
* The type Objective completion record.
*
* @author felord.cn
* @since 1.0.2.RELEASE
*/
@Data
public static class ObjectiveCompletionRecord {
/**
* The Completion count.
*/
private Long completionCount;
/**
* The Completion time.
*/
private String completionTime;
/**
* The Completion type.
*/
private StrategyType completionType;
/**
* The Description.
*/
private String description;
/**
* The Objective completion serial no.
*/
private String objectiveCompletionSerialNo;
/**
* The Objective id.
*/
private String objectiveId;
/**
* The Remark.
*/
private String remark;
}
/**
* The type Reward usage record.
*
* @author felord.cn
* @since 1.0.2.RELEASE
*/
@Data
public static class RewardUsageRecord {
/**
* The Amount.
*/
private Long amount;
/**
* The Description.
*/
private String description;
/**
* The Remark.
*/
private String remark;
/**
* The Reward id.
*/
private String rewardId;
/**
* The Reward usage serial no.
*/
private String rewardUsageSerialNo;
/**
* The Usage count.
*/
private Long usageCount;
/**
* The Usage time.
*/
private String usageTime;
/**
* The Usage type.
*/
private StrategyType usageType;
}
/**
* 目标完成类型、优惠使用类型
*
* @author felord.cn
* @since 1.0.2.RELEASE
*/
public enum StrategyType {
/**
* 增加数量,表示用户发生了履约行为
*/
INCREASE,
/**
* 减少数量,表示取消用户的履约行为(例如用户取消购买、退货退款等)
*/
DECREASE
}
}