From efb2c52011fb25575065b9d9b9ce73b7ad360078 Mon Sep 17 00:00:00 2001 From: thinsstar <43289204+thinsstar@users.noreply.github.com> Date: Fri, 21 May 2021 22:07:02 +0800 Subject: [PATCH] =?UTF-8?q?:new:=20#2130=20=E3=80=90=E5=BE=AE=E4=BF=A1?= =?UTF-8?q?=E6=94=AF=E4=BB=98=E3=80=91=E5=A2=9E=E5=8A=A0=E5=BE=AE=E4=BF=A1?= =?UTF-8?q?=E6=94=AF=E4=BB=98=E9=83=A8=E5=88=86v3=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bean/notify/OriginNotifyResponse.java | 165 +++++ .../wxpay/bean/notify/SignatureHeader.java | 32 + .../bean/notify/WxPayOrderNotifyV3Result.java | 542 +++++++++++++++++ .../notify/WxPayRefundNotifyV3Result.java | 214 +++++++ .../request/WxPayOrderCloseV3Request.java | 47 ++ .../request/WxPayOrderQueryV3Request.java | 62 ++ .../request/WxPayRefundQueryV3Request.java | 34 ++ .../bean/request/WxPayRefundV3Request.java | 240 ++++++++ .../request/WxPayUnifiedOrderV3Request.java | 565 ++++++++++++++++++ .../bean/result/WxPayOrderQueryV3Result.java | 528 ++++++++++++++++ .../bean/result/WxPayRefundQueryV3Result.java | 473 +++++++++++++++ .../bean/result/WxPayRefundV3Result.java | 474 +++++++++++++++ .../result/WxPayUnifiedOrderV3Result.java | 124 ++++ .../bean/result/enums/TradeTypeEnum.java | 33 + .../wxpay/service/BasePayV3Service.java | 24 + .../wxpay/service/WxPayService.java | 184 +++++- .../service/impl/BaseWxPayServiceImpl.java | 153 ++++- .../impl/BaseWxPayServiceImplTest.java | 71 +++ 18 files changed, 3959 insertions(+), 6 deletions(-) create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/OriginNotifyResponse.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/SignatureHeader.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayOrderNotifyV3Result.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyV3Result.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderCloseV3Request.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderQueryV3Request.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundQueryV3Request.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundV3Request.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayUnifiedOrderV3Request.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderQueryV3Result.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundQueryV3Result.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundV3Result.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayUnifiedOrderV3Result.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/enums/TradeTypeEnum.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/BasePayV3Service.java diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/OriginNotifyResponse.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/OriginNotifyResponse.java new file mode 100644 index 0000000000..7efd9adb06 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/OriginNotifyResponse.java @@ -0,0 +1,165 @@ +package com.github.binarywang.wxpay.bean.notify; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +@Data +@NoArgsConstructor +public class OriginNotifyResponse implements Serializable { + private static final long serialVersionUID = 1L; + /** + *
+ * 字段名:通知ID + * 变量名:id + * 是否必填:是 + * 类型:string[1,36] + * 描述: + * 通知的唯一ID + * 示例值:EV-2018022511223320873 + *+ */ + @SerializedName(value = "id") + private String id; + /** + *
+ * 字段名:通知创建时间 + * 变量名:create_time + * 是否必填:是 + * 类型:string[1,32] + * 描述: + * 通知创建的时间,遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss表示时分秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35+08:00表示,北京时间2015年5月20日13点29分35秒。 + * 示例值:2018-06-08T10:34:56+08:00 + *+ */ + @SerializedName(value = "create_time") + private String createTime; + /** + *
+ * 字段名:通知类型 + * 变量名:event_type + * 是否必填:是 + * 类型:string[1,32] + * 描述: + * 通知的类型: + * REFUND.SUCCESS:退款成功通知 + * REFUND.ABNORMAL:退款异常通知 + * REFUND.CLOSED:退款关闭通知 + * 示例值:REFUND.SUCCESS + *+ */ + @SerializedName(value = "event_type") + private String eventType; + /** + *
+ * 字段名:通知简要说明 + * 变量名:summary + * 是否必填:是 + * 类型:string[1,16] + * 描述: + * 通知简要说明 + * 示例值:退款成功 + *+ */ + @SerializedName(value = "summary") + private String summary; + /** + *
+ * 字段名:通知数据类型 + * 变量名:resource_type + * 是否必填:是 + * 类型:string[1,32] + * 描述: + * 通知的资源数据类型,支付成功通知为encrypt-resource + * 示例值:encrypt-resource + *+ */ + @SerializedName(value = "resource_type") + private String resourceType; + /** + *
+ * 字段名:通知数据 + * 变量名:resource + * 是否必填:是 + * 类型:object + * 描述: + * 通知资源数据 + * json格式,见示例 + *+ */ + @SerializedName(value = "resource") + private Resource resource; + + @Data + @NoArgsConstructor + public static class Resource implements Serializable { + private static final long serialVersionUID = 1L; + /** + *
+ * 字段名:加密算法类型 + * 变量名:algorithm + * 是否必填:是 + * 类型:string[1,32] + * 描述: + * 对开启结果数据进行加密的加密算法,目前只支持AEAD_AES_256_GCM + * 示例值:AEAD_AES_256_GCM + *+ */ + @SerializedName(value = "algorithm") + private String algorithm; + /** + *
+ * 字段名:原始类型 + * 变量名:original_type + * 是否必填:是 + * 类型:string[1,16] + * 描述: + * 原始回调类型,为transaction + * 示例值:transaction + *+ */ + @SerializedName(value = "original_type") + private String originalType; + /** + *
+ * 字段名:数据密文 + * 变量名:ciphertext + * 是否必填:是 + * 类型:string[1,1048576] + * 描述: + * Base64编码后的开启/停用结果数据密文 + * 示例值:sadsadsadsad + *+ */ + @SerializedName(value = "ciphertext") + private String ciphertext; + /** + *
+ * 字段名:附加数据 + * 变量名:associated_data + * 是否必填:否 + * 类型:string[1,16] + * 描述: + * 附加数据 + * 示例值:fdasfwqewlkja484w + *+ */ + @SerializedName(value = "associated_data") + private String associatedData; + /** + *
+ * 字段名:随机串 + * 变量名:nonce + * 是否必填:是 + * 类型:string[1,16] + * 描述: + * 加密使用的随机串 + * 示例值:fdasflkja484w + *+ */ + @SerializedName(value = "nonce") + private String nonce; + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/SignatureHeader.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/SignatureHeader.java new file mode 100644 index 0000000000..d010637a8c --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/SignatureHeader.java @@ -0,0 +1,32 @@ +package com.github.binarywang.wxpay.bean.notify; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 微信通知接口头部信息,需要做签名验证 + * 文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_1.shtml + */ +@Data +@NoArgsConstructor +public class SignatureHeader implements Serializable { + private static final long serialVersionUID = -1L; + /** + * 时间戳 + */ + private String timeStamp; + /** + * 随机串 + */ + private String nonce; + /** + * 已签名字符串 + */ + private String signature; + /** + * 证书序列号 + */ + private String serial; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayOrderNotifyV3Result.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayOrderNotifyV3Result.java new file mode 100644 index 0000000000..549e2af16c --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayOrderNotifyV3Result.java @@ -0,0 +1,542 @@ +package com.github.binarywang.wxpay.bean.notify; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +/** + * 支付结果通知. + * 文档见:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_5.shtml + * + * @author thinsstar + */ +@Data +@NoArgsConstructor +public class WxPayOrderNotifyV3Result implements Serializable { + private static final long serialVersionUID = -1L; + /** + * 源数据 + */ + private OriginNotifyResponse rawData; + /** + * 解密后的数据 + */ + private DecryptNotifyResult result; + + @Data + @NoArgsConstructor + public static class DecryptNotifyResult implements Serializable { + private static final long serialVersionUID = 1L; + /** + *
+ * 字段名:应用ID + * 变量名:appid + * 是否必填:是 + * 类型:string[1,32] + * 描述: + * 直连商户申请的公众号或移动应用appid。 + * 示例值:wxd678efh567hg6787 + *+ */ + @SerializedName(value = "appid") + private String appid; + + /** + *
+ * 字段名:商户号 + * 变量名:mchid + * 是否必填:是 + * 类型:string[1,32] + * 描述: + * 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一。 + * 特殊规则:最小字符长度为6 + * 示例值:1217752501201407033233368018 + *+ */ + @SerializedName(value = "mchid") + private String mchid; + /** + *
+ * 字段名:商户订单号 + * 变量名:out_trade_no + * 是否必填:是 + * 类型:string[6,32] + * 描述: + * 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一。 + * 特殊规则:最小字符长度为6 + * 示例值:1217752501201407033233368018 + *+ */ + @SerializedName(value = "out_trade_no") + private String outTradeNo; + /** + *
+ * 字段名:微信支付订单号 + * 变量名:transaction_id + * 是否必填:是 + * 类型:string[1,32] + * 描述: + * 微信支付系统生成的订单号。 + * 示例值:1217752501201407033233368018 + *+ */ + @SerializedName(value = "transaction_id") + private String transactionId; + /** + *
+ * 字段名:交易类型 + * 变量名:trade_type + * 是否必填:是 + * 类型:string[1,16] + * 描述: + * 交易类型,枚举值: + * JSAPI:公众号支付 + * NATIVE:扫码支付 + * APP:APP支付 + * MICROPAY:付款码支付 + * MWEB:H5支付 + * FACEPAY:刷脸支付 + * 示例值:MICROPAY + *+ */ + @SerializedName(value = "trade_type") + private String tradeType; + /** + *
+ * 字段名:交易状态 + * 变量名:trade_state + * 是否必填:是 + * 类型:string[1,32] + * 描述: + * 交易状态,枚举值: + * SUCCESS:支付成功 + * REFUND:转入退款 + * NOTPAY:未支付 + * CLOSED:已关闭 + * REVOKED:已撤销(付款码支付) + * USERPAYING:用户支付中(付款码支付) + * PAYERROR:支付失败(其他原因,如银行返回失败) + * 示例值:SUCCESS + *+ */ + @SerializedName(value = "trade_state") + private String tradeState; + /** + *
+ * 字段名:交易状态描述 + * 变量名:trade_state_desc + * 是否必填:是 + * 类型:string[1,256] + * 描述: + * 交易状态描述 + * 示例值:支付成功 + *+ */ + @SerializedName(value = "trade_state_desc") + private String tradeStateDesc; + /** + *
+ * 字段名:付款银行 + * 变量名:bank_type + * 是否必填:是 + * 类型:string[1,16] + * 描述: + * 银行类型,采用字符串类型的银行标识。银行标识请参考《银行类型对照表》https://pay.weixin.qq.com/wiki/doc/apiv3/terms_definition/chapter1_1_3.shtml#part-6 + * 示例值:CMC + *+ */ + @SerializedName(value = "bank_type") + private String bankType; + /** + *
+ * 字段名:附加数据 + * 变量名:attach + * 是否必填:否 + * 类型:string[1,128] + * 描述: + * 附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用 + * 示例值:自定义数据 + *+ */ + @SerializedName(value = "attach") + private String attach; + /** + *
+ * 字段名:支付完成时间 + * 变量名:success_time + * 是否必填:是 + * 类型:string[1,64] + * 描述: + * 支付完成时间,遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss表示时分秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35+08:00表示,北京时间2015年5月20日 13点29分35秒。 + * 示例值:2018-06-08T10:34:56+08:00 + *+ */ + @SerializedName(value = "success_time") + private String successTime; + /** + *
+ * 字段名:支付者 + * 变量名:payer + * 是否必填:是 + * 类型:object + * 描述: + * 支付者信息 + *+ */ + private Payer payer; + /** + *
+ * 字段名:订单金额 + * 变量名:amount + * 是否必填:否 + * 类型:object + * 描述: + * 订单金额信息 + *+ */ + @SerializedName(value = "amount") + private Amount amount; + /** + *
+ * 字段名:场景信息 + * 变量名:scene_info + * 是否必填:否 + * 类型:object + * 描述: + * 支付场景信息描述 + *+ */ + @SerializedName(value = "scene_info") + private SceneInfo sceneInfo; + /** + *
+ * 字段名:优惠功能 + * 变量名:promotion_detail + * 是否必填:否 + * 类型:array + * 描述: + * 优惠功能,享受优惠时返回该字段。 + *+ */ + @SerializedName(value = "promotion_detail") + private List
+ * 字段名:用户标识 + * 变量名:openid + * 是否必填:是 + * 类型:string[1,128] + * 描述: + * 用户在直连商户appid下的唯一标识。 + * 示例值:oUpF8uMuAJO_M2pxb1Q9zNjWeS6o + *+ */ + @SerializedName(value = "openid") + private String openid; + } + + @Data + @NoArgsConstructor + public static class Amount implements Serializable { + private static final long serialVersionUID = 1L; + /** + *
+ * 字段名:总金额 + * 变量名:total + * 是否必填:否 + * 类型:int + * 描述: + * 订单总金额,单位为分。 + * 示例值:100 + *+ */ + @SerializedName(value = "total") + private Integer total; + /** + *
+ * 字段名:用户支付金额 + * 变量名:payer_total + * 是否必填:否 + * 类型:int + * 描述: + * 用户支付金额,单位为分。 + * 示例值:100 + *+ */ + @SerializedName(value = "payer_total") + private Integer payerTotal; + /** + *
+ * 字段名:货币类型 + * 变量名:currency + * 是否必填:否 + * 类型:string[1,16] + * 描述: + * CNY:人民币,境内商户号仅支持人民币。 + * 示例值:CNY + *+ */ + @SerializedName(value = "currency") + private String currency; + /** + *
+ * 字段名:用户支付币种 + * 变量名:payer_currency + * 是否必填:否 + * 类型:string[1,16] + * 描述: + * 用户支付币种 + * 示例值: CNY + *+ */ + @SerializedName(value = "payer_currency") + private String payerCurrency; + } + + @Data + @NoArgsConstructor + public static class SceneInfo implements Serializable { + private static final long serialVersionUID = 1L; + /** + *
+ * 字段名:商户端设备号 + * 变量名:device_id + * 是否必填:否 + * 类型:string[1,32] + * 描述: + * 终端设备号(门店号或收银设备ID)。 + * 示例值:013467007045764 + *+ */ + @SerializedName(value = "device_id") + private String deviceId; + } + + @Data + @NoArgsConstructor + public static class PromotionDetail implements Serializable { + /** + *
+ * 字段名:券ID + * 变量名:coupon_id + * 是否必填:是 + * 类型:string[1,32] + * 描述: + * 券ID + * 示例值:109519 + *+ */ + @SerializedName(value = "coupon_id") + private String couponId; + /** + *
+ * 字段名:优惠名称 + * 变量名:name + * 是否必填:否 + * 类型:string[1,64] + * 描述: + * 优惠名称 + * 示例值:单品惠-6 + *+ */ + @SerializedName(value = "name") + private String name; + /** + *
+ * 字段名:优惠范围 + * 变量名:scope + * 是否必填:否 + * 类型:string[1,32] + * 描述: + * GLOBAL:全场代金券 + * SINGLE:单品优惠 + * 示例值:GLOBAL + *+ */ + @SerializedName(value = "scope") + private String scope; + /** + *
+ * 字段名:优惠类型 + * 变量名:type + * 是否必填:否 + * 类型:string[1,32] + * 描述: + * CASH:充值 + * NOCASH:预充值 + * 示例值:CASH + *+ */ + @SerializedName(value = "type") + private String type; + /** + *
+ * 字段名:优惠券面额 + * 变量名:amount + * 是否必填:是 + * 类型:int + * 描述: + * 优惠券面额 + * 示例值:100 + *+ */ + @SerializedName(value = "amount") + private Integer amount; + /** + *
+ * 字段名:活动ID + * 变量名:stock_id + * 是否必填:否 + * 类型:string[1,32] + * 描述: + * 活动ID + * 示例值:931386 + *+ */ + @SerializedName(value = "stock_id") + private String stockId; + /** + *
+ * 字段名:微信出资 + * 变量名:wechatpay_contribute + * 是否必填:否 + * 类型:int + * 描述: + * 微信出资,单位为分 + * 示例值:0 + *+ */ + @SerializedName(value = "wechatpay_contribute") + private Integer wechatpayContribute; + /** + *
+ * 字段名:商户出资 + * 变量名:merchant_contribute + * 是否必填:否 + * 类型:int + * 描述: + * 商户出资,单位为分 + * 示例值:0 + *+ */ + @SerializedName(value = "merchant_contribute") + private Integer merchantContribute; + /** + *
+ * 字段名:其他出资 + * 变量名:other_contribute + * 是否必填:否 + * 类型:int + * 描述: + * 其他出资,单位为分 + * 示例值:0 + *+ */ + @SerializedName(value = "other_contribute") + private Integer otherContribute; + /** + *
+ * 字段名:优惠币种 + * 变量名:currency + * 是否必填:否 + * 类型:string[1,16] + * 描述: + * CNY:人民币,境内商户号仅支持人民币。 + * 示例值:CNY + *+ */ + @SerializedName(value = "currency") + private String currency; + /** + *
+ * 字段名:单品列表 + * 变量名:goods_detail + * 是否必填:否 + * 类型:array + * 描述: + * 单品列表信息 + *+ */ + @SerializedName(value = "goods_detail") + private List
+ * 字段名:商品编码 + * 变量名:goods_id + * 是否必填:是 + * 类型:string[1,32] + * 描述: + * 商品编码 + * 示例值:M1006 + *+ */ + @SerializedName(value = "goods_id") + private String goodsId; + /** + *
+ * 字段名:商品数量 + * 变量名:quantity + * 是否必填:是 + * 类型:int + * 描述: + * 用户购买的数量 + * 示例值:1 + *+ */ + @SerializedName(value = "quantity") + private Integer quantity; + /** + *
+ * 字段名:商品单价 + * 变量名:unit_price + * 是否必填:是 + * 类型:int + * 描述: + * 商品单价,单位为分 + * 示例值:100 + *+ */ + @SerializedName(value = "unit_price") + private Integer unitPrice; + /** + *
+ * 字段名:商品优惠金额 + * 变量名:discount_amount + * 是否必填:是 + * 类型:int + * 描述: + * 商品优惠金额 + * 示例值:0 + *+ */ + @SerializedName(value = "discount_amount") + private Integer discountAmount; + /** + *
+ * 字段名:商品备注 + * 变量名:goods_remark + * 是否必填:否 + * 类型:string[1,128] + * 描述: + * 商品备注信息 + * 示例值:商品备注信息 + *+ */ + @SerializedName(value = "goods_remark") + private String goodsRemark; + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyV3Result.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyV3Result.java new file mode 100644 index 0000000000..39aafe364b --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyV3Result.java @@ -0,0 +1,214 @@ +package com.github.binarywang.wxpay.bean.notify; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 退款结果通知. + * 文档见:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_11.shtml + * + * @author thinsstar + */ +@Data +@NoArgsConstructor +public class WxPayRefundNotifyV3Result implements Serializable { + private static final long serialVersionUID = -1L; + /** + * 源数据 + */ + private OriginNotifyResponse rawData; + /** + * 解密后的数据 + */ + private DecryptNotifyResult result; + + @Data + @NoArgsConstructor + public static class DecryptNotifyResult implements Serializable { + private static final long serialVersionUID = -1L; + /** + *
+ * 字段名:直连商户号 + * 变量名:mchid + * 是否必填:是 + * 类型:string[1,32] + * 描述: + * 直连商户的商户号,由微信支付生成并下发。 + * 示例值:1900000100 + *+ */ + @SerializedName(value = "mchid") + private String mchid; + /** + *
+ * 字段名:商户订单号 + * 变量名:out_trade_no + * 是否必填:是 + * 类型:string[1,32] + * 描述: + * 返回的商户订单号 + * 示例值: 1217752501201407033233368018 + *+ */ + @SerializedName(value = "out_trade_no") + private String outTradeNo; + /** + *
+ * 字段名:微信支付订单号 + * 变量名:transaction_id + * 是否必填:是 + * 类型:string[1,32] + * 描述: + * 微信支付订单号 + * 示例值: 1217752501201407033233368018 + *+ */ + @SerializedName(value = "transaction_id") + private String transactionId; + /** + *
+ * 字段名:商户退款单号 + * 变量名:out_refund_no + * 是否必填:是 + * 类型:string[1,64] + * 描述: + * 商户退款单号 + * 示例值: 1217752501201407033233368018 + *+ */ + @SerializedName(value = "out_refund_no") + private String outRefundNo; + /** + *
+ * 字段名:微信支付退款号 + * 变量名:refund_id + * 是否必填:是 + * 类型:string[1,32] + * 描述: + * 微信退款单号 + * 示例值: 1217752501201407033233368018 + *+ */ + @SerializedName(value = "refund_id") + private String refundId; + /** + *
+ * 字段名:退款状态 + * 变量名:refund_status + * 是否必填:是 + * 类型:string[1,16] + * 描述: + * 退款状态,枚举值: + * SUCCESS:退款成功 + * CLOSE:退款关闭 + * ABNORMAL:退款异常,退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,可前往【商户平台—>交易中心】,手动处理此笔退款 + * 示例值:SUCCESS + *+ */ + @SerializedName(value = "refund_status") + private String refundStatus; + /** + *
+ * 字段名:退款成功时间 + * 变量名:success_time + * 是否必填:否 + * 类型:string[1,64] + * 描述: + * 1、退款成功时间,遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss表示时分秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35+08:00表示,北京时间2015年5月20日13点29分35秒。 + * 2、当退款状态为退款成功时返回此参数。 + * 示例值:2018-06-08T10:34:56+08:00 + *+ */ + @SerializedName(value = "success_time") + private String successTime; + /** + *
+ * 字段名:退款入账账户 + * 变量名:user_received_account + * 是否必填:是 + * 类型:string[1,64] + * 描述: + * 取当前退款单的退款入账方。 + * 1、退回银行卡:{银行名称}{卡类型}{卡尾号} + * 2、退回支付用户零钱: 支付用户零钱 + * 3、退还商户: 商户基本账户、商户结算银行账户 + * 4、退回支付用户零钱通:支付用户零钱通 + * 示例值:招商银行信用卡0403 + *+ */ + @SerializedName(value = "user_received_account") + private String userReceivedAccount; + /** + *
+ * 字段名:金额信息 + * 变量名:amount + * 是否必填:是 + * 类型:object + * 描述: + * 金额信息 + *+ */ + @SerializedName(value = "amount") + private Amount amount; + } + + @Data + @NoArgsConstructor + public static class Amount implements Serializable { + /** + *
+ * 字段名:订单金额 + * 变量名:total + * 是否必填:是 + * 类型:int + * 描述: + * 订单总金额,单位为分,只能为整数,详见支付金额 + * 示例值:999 + *+ */ + @SerializedName(value = "total") + private Integer total; + /** + *
+ * 字段名:退款金额 + * 变量名:refund + * 是否必填:是 + * 类型:int + * 描述: + * 退款金额,币种的最小单位,只能为整数,不能超过原订单支付金额,如果有使用券,后台会按比例退。 + * 示例值:999 + *+ */ + @SerializedName(value = "refund") + private String refund; + /** + *
+ * 字段名:用户支付金额 + * 变量名:payer_total + * 是否必填:是 + * 类型:int + * 描述: + * 用户实际支付金额,单位为分,只能为整数,详见支付金额 + * 示例值:999 + *+ */ + @SerializedName(value = "payer_total") + private Integer payerTotal; + /** + *
+ * 字段名:用户退款金额 + * 变量名:payer_refund + * 是否必填:是 + * 类型:int + * 描述: + * 退款给用户的金额,不包含所有优惠券金额 + * 示例值:999 + *+ */ + @SerializedName(value = "payer_refund") + private String payerRefund; + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderCloseV3Request.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderCloseV3Request.java new file mode 100644 index 0000000000..8031d7a25f --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderCloseV3Request.java @@ -0,0 +1,47 @@ +package com.github.binarywang.wxpay.bean.request; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; + +/** + *
+ * 关闭订单请求对象类 + *+ * + * @author thinsstar + */ +@Data +@NoArgsConstructor +@Accessors(chain = true) +public class WxPayOrderCloseV3Request implements Serializable { + private static final long serialVersionUID = 1L; + /** + *
+ * 字段名:直连商户号 + * 变量名:mchid + * 是否必填:是 + * 类型:string[1,32] + * 描述: + * 直连商户的商户号,由微信支付生成并下发。 + * 示例值:1230000109 + *+ */ + @SerializedName(value = "mchid") + private String mchid; + /** + *
+ * 字段名:商户订单号 + * 变量名:out_trade_no + * 是否必填:是 + * 类型:string[6,32] + * 描述: + * 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一 + * 示例值:1217752501201407033233368018 + *+ */ + private transient String outTradeNo; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderQueryV3Request.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderQueryV3Request.java new file mode 100644 index 0000000000..9dd1944ba6 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderQueryV3Request.java @@ -0,0 +1,62 @@ +package com.github.binarywang.wxpay.bean.request; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; + +/** + *
+ * 订单查询请求对象 + *+ * + * @author thinsstar + */ +@Data +@NoArgsConstructor +@Accessors(chain = true) +public class WxPayOrderQueryV3Request implements Serializable { + private static final long serialVersionUID = 1L; + /** + *
+ * 字段名:直连商户号 + * 变量名:mchid + * 是否必填:是 + * 类型:string[1,32] + * 描述: + * 直连商户的商户号,由微信支付生成并下发。 + * 示例值:1230000109 + *+ */ + @SerializedName(value = "mchid") + private String mchid; + /** + *
+ * 字段名:微信支付订单号 + * 变量名:transaction_id + * 是否必填:是 + * 类型:string[1,32] + * 描述: + * 微信支付系统生成的订单号 + * 示例值:1217752501201407033233368018 + *+ */ + @SerializedName(value = "transaction_id") + private String transactionId; + /** + *
+ * 字段名:商户订单号 + * 变量名:out_trade_no + * 是否必填:是 + * 类型:string[1,32] + * 描述: + * 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一。 + * 特殊规则:最小字符长度为6 + * 示例值:1217752501201407033233368018 + *+ */ + @SerializedName(value = "out_trade_no") + private String outTradeNo; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundQueryV3Request.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundQueryV3Request.java new file mode 100644 index 0000000000..d29f41a4c0 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundQueryV3Request.java @@ -0,0 +1,34 @@ +package com.github.binarywang.wxpay.bean.request; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + *
+ * 微信支付-查询单笔退款API + *+ * + * @author thinsstar + */ +@Data +@NoArgsConstructor +public class WxPayRefundQueryV3Request implements Serializable { + private static final long serialVersionUID = 1L; + /** + *
+ * 字段名:商户订单号 + * 变量名:out_trade_no + * 是否必填:是 + * 类型:string[1,32] + * 描述: + * 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一。 + * 特殊规则:最小字符长度为6 + * 示例值:1217752501201407033233368018 + *+ */ + @SerializedName(value = "out_trade_no") + private String outTradeNo; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundV3Request.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundV3Request.java new file mode 100644 index 0000000000..da90306de4 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundV3Request.java @@ -0,0 +1,240 @@ +package com.github.binarywang.wxpay.bean.request; + +import com.google.gson.annotations.SerializedName; +import lombok.*; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.List; + +/** + *
+ * 微信支付-申请退款请求参数 + *+ * + * @author thinsstar + */ +@Data +@NoArgsConstructor +@Accessors(chain = true) +public class WxPayRefundV3Request implements Serializable { + private static final long serialVersionUID = -1L; + /** + *
+ * 字段名:微信支付订单号 + * 变量名:transaction_id + * 是否必填:与out_order_no二选一 + * 类型:string[1, 32] + * 描述: + * 原支付交易对应的微信订单号。 + * 示例值:1217752501201407033233368018 + *+ */ + @SerializedName(value = "transaction_id") + private String transactionId; + /** + *
+ * 字段名:商户订单号 + * 变量名:out_trade_no + * 是否必填:与transaction_id二选一 + * 类型:string[1, 32] + * 描述: + * 原支付交易对应的商户订单号。 + * 示例值:1217752501201407033233368018 + *+ */ + @SerializedName(value = "out_trade_no") + private String outTradeNo; + /** + *
+ * 字段名:商户退款单号 + * 变量名:out_refund_no + * 是否必填:是 + * 类型:string[1, 64] + * 描述: + * 商户系统内部的退款单号,商户系统内部唯一,只能是数字、大小写字母_-|*@ ,同一退款单号多次请求只退一笔。 + * 示例值:1217752501201407033233368018 + *+ */ + @SerializedName(value = "out_refund_no") + private String outRefundNo; + /** + *
+ * 字段名:退款原因 + * 变量名:reason + * 是否必填:否 + * 类型:string[1, 80] + * 描述: + * 若商户传入,会在下发给用户的退款消息中体现退款原因。 + * 示例值:商品已售完 + *+ */ + @SerializedName(value = "reason") + private String reason; + /** + *
+ * 字段名:退款结果回调url + * 变量名:notify_url + * 是否必填:否 + * 类型:string[8, 256] + * 描述: + * 异步接收微信支付退款结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。 如果参数中传了notify_url,则商户平台上配置的回调地址将不会生效,优先回调当前传的这个地址。 + * 示例值:https://weixin.qq.com + *+ */ + @SerializedName(value = "notify_url") + private String notifyUrl; + /** + *
+ * 字段名:订单金额 + * 变量名:amount + * 是否必填:是 + * 类型:object + * 描述: + * 订单金额信息 + *+ */ + @SerializedName(value = "amount") + private Amount amount; + /** + *
+ * 字段名:退款商品 + * 变量名:goods_detail + * 是否必填:否 + * 类型:array + * 描述: + * 指定商品退款需要传此参数,其他场景无需传递。 + *+ */ + @SerializedName(value = "goods_detail") + private List
+ * 字段名:退款金额 + * 变量名:refund + * 是否必填:是 + * 类型:int + * 描述: + * 退款金额,币种的最小单位,只能为整数,不能超过原订单支付金额。 + * 示例值:888 + *+ */ + @SerializedName(value = "refund") + private Integer refund; + /** + *
+ * 字段名:原订单金额 + * 变量名:total + * 是否必填:是 + * 类型:int + * 描述: + * 原支付交易的订单总金额,币种的最小单位,只能为整数。 + * 示例值:888 + *+ */ + @SerializedName(value = "total") + private Integer total; + /** + *
+ * 字段名:币类型 + * 变量名:currency + * 是否必填:否 + * 类型:string[1, 16] + * 描述: + * 符合ISO 4217标准的三位字母代码,目前只支持人民币:CNY。 + * 示例值:CNY + *+ */ + @SerializedName(value = "currency") + private String currency; + } + + @Data + @NoArgsConstructor + public static class GoodsDetail implements Serializable { + private static final long serialVersionUID = -1L; + /** + *
+ * 字段名:商户侧商品编码 + * 变量名:merchant_goods_id + * 是否必填:是 + * 类型:string[1,32] + * 描述: + * 由半角的大小写字母、数字、中划线、下划线中的一种或几种组成。 + * 示例值:1217752501201407033233368018 + *+ */ + @SerializedName(value = "merchant_goods_id") + private String merchantGoodsId; + /** + *
+ * 字段名:微信侧商品编码 + * 变量名:wechatpay_goods_id + * 是否必填:否 + * 类型:string[1,32] + * 描述: + * 微信支付定义的统一商品编号(没有可不传)。 + * 示例值:1001 + *+ */ + @SerializedName(value = "wechatpay_goods_id") + private String wechatpayGoodsId; + /** + *
+ * 字段名:商品名称 + * 变量名:goods_name + * 是否必填:否 + * 类型:string[1,256] + * 描述: + * 商品的实际名称。 + * 示例值:iPhone6s 16G + *+ */ + @SerializedName(value = "goods_name") + private String goodsName; + /** + *
+ * 字段名:商品单价 + * 变量名:unit_price + * 是否必填:是 + * 类型:int + * 描述: + * 商品单价金额,单位为分。 + * 示例值:528800 + *+ */ + @SerializedName(value = "unit_price") + private Integer unitPrice; + /** + *
+ * 字段名:商品退款金额 + * 变量名:refund_amount + * 是否必填:是 + * 类型:int + * 描述: + * 商品退款金额,单位为分。 + * 示例值:528800 + *+ */ + @SerializedName(value = "refund_amount") + private Integer refundAmount; + /** + *
+ * 字段名:商品退货数量 + * 变量名:refund_quantity + * 是否必填:是 + * 类型:int + * 描述: + * 单品的退款数量。 + * 示例值:1 + *+ */ + @SerializedName(value = "refund_quantity") + private Integer refundQuantity; + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayUnifiedOrderV3Request.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayUnifiedOrderV3Request.java new file mode 100644 index 0000000000..c0bf417825 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayUnifiedOrderV3Request.java @@ -0,0 +1,565 @@ +package com.github.binarywang.wxpay.bean.request; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.List; + +/** + *
+ * 统一下单请求参数对象. + * 参考文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_1.shtml + * https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_2_1.shtml + * https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_3_1.shtml + * https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_1.shtml + * https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_1.shtml + *+ * + * @author thinsstar + */ +@Data +@NoArgsConstructor +@Accessors(chain = true) +public class WxPayUnifiedOrderV3Request implements Serializable { + private static final long serialVersionUID = 1L; + /** + *
+ * 字段名:应用ID + * 变量名:appid + * 是否必填:是 + * 类型:string[1,32] + * 描述: + * 由微信生成的应用ID,全局唯一。请求统一下单接口时请注意APPID的应用属性,例如公众号场景下,需使用应用属性为公众号的APPID + * 示例值:wxd678efh567hg6787 + *+ */ + @SerializedName(value = "appid") + protected String appid; + /** + *
+ * 字段名:直连商户号 + * 变量名:mchid + * 是否必填:是 + * 类型:string[1,32] + * 描述: + * 直连商户的商户号,由微信支付生成并下发。 + * 示例值:1230000109 + *+ */ + @SerializedName(value = "mchid") + protected String mchid; + /** + *
+ * 字段名:商品描述 + * 变量名:description + * 是否必填:是 + * 类型:string[1,127] + * 描述: + * 商品描述 + * 示例值:Image形象店-深圳腾大-QQ公仔 + *+ */ + @SerializedName(value = "description") + protected String description; + /** + *
+ * 字段名:商户订单号 + * 变量名:out_trade_no + * 是否必填:是 + * 类型:string[6,32] + * 描述: + * 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一 + * 示例值:1217752501201407033233368018 + *+ */ + @SerializedName(value = "out_trade_no") + protected String outTradeNo; + /** + *
+ * 字段名:交易结束时间 + * 变量名:out_trade_no + * 是否必填:是 + * 类型:string[1,64] + * 描述: + * 订单失效时间,遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss表示时分秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35+08:00表示,北京时间2015年5月20日 13点29分35秒。 + * 示例值:2018-06-08T10:34:56+08:00 + *+ */ + @SerializedName(value = "time_expire") + protected String timeExpire; + /** + *
+ * 字段名:附加数据 + * 变量名:attach + * 是否必填:否 + * 类型:string[1,128] + * 描述: + * 附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用 + * 示例值:自定义数据 + *+ */ + @SerializedName(value = "attach") + protected String attach; + /** + *
+ * 字段名:通知地址 + * 变量名:notify_url + * 是否必填:是 + * 类型:string[1,256] + * 描述: + * 通知URL必须为直接可访问的URL,不允许携带查询串,要求必须为https地址。 + * 格式:URL + * 示例值:https://www.weixin.qq.com/wxpay/pay.php + *+ */ + @SerializedName(value = "notify_url") + private String notifyUrl; + /** + *
+ * 字段名:订单优惠标记 + * 变量名:goods_tag + * 是否必填:否 + * 类型:string[1,256] + * 描述: + * 订单优惠标记 + * 示例值:WXG + *+ */ + @SerializedName(value = "goods_tag") + private String goodsTag; + /** + *
+ * 字段名:订单金额 + * 变量名:amount + * 是否必填:是 + * 类型:object + * 描述: + * 订单金额信息 + *+ */ + @SerializedName(value = "amount") + private Amount amount; + /** + *
+ * 字段名:支付者 + * 变量名:payer + * 是否必填:是 + * 类型:object + * 描述: + * 支付者信息 + *+ */ + @SerializedName(value = "payer") + private Payer payer; + /** + *
+ * 字段名:优惠功能 + * 变量名:detail + * 是否必填:否 + * 类型:object + * 描述: + * 优惠功能 + *+ */ + @SerializedName(value = "detail") + private Discount detail; + /** + *
+ * 字段名:场景信息 + * 变量名:scene_info + * 是否必填:否 + * 类型:object + * 描述: + * 支付场景描述 + *+ */ + @SerializedName(value = "scene_info") + private SceneInfo sceneInfo; + /** + *
+ * 字段名:结算信息 + * 变量名:settle_info + * 是否必填:否 + * 类型:Object + * 描述:结算信息 + *+ */ + @SerializedName(value = "settle_info") + private SettleInfo settleInfo; + + @Data + @NoArgsConstructor + public static class Amount implements Serializable { + private static final long serialVersionUID = 1L; + /** + *
+ * 字段名:总金额 + * 变量名:total + * 是否必填:是 + * 类型:int + * 描述: + * 订单总金额,单位为分。 + * 示例值:100 + *+ */ + @SerializedName(value = "total") + private Integer total; + /** + *
+ * 字段名:币类型 + * 变量名:currency + * 是否必填:否 + * 类型:string[1,16] + * 描述: + * CNY:人民币,境内商户号仅支持人民币。 + * 示例值:CNY + *+ */ + @SerializedName(value = "currency") + private String currency; + } + + @Data + @NoArgsConstructor + public static class Payer implements Serializable { + private static final long serialVersionUID = -1L; + /** + *
+ * 字段名:用户标识 + * 变量名:openid + * 是否必填:是 + * 类型:string[1,128] + * 描述: + * 用户在直连商户appid下的唯一标识。 + * 示例值:oUpF8uMuAJO_M2pxb1Q9zNjWeS6o + *+ */ + @SerializedName(value = "openid") + private String openid; + } + + @Data + @NoArgsConstructor + public static class Discount implements Serializable { + private static final long serialVersionUID = 1L; + /** + *
+ * 字段名:订单原价 + * 变量名:cost_price + * 是否必填:否 + * 类型:int + * 描述: + * 1、商户侧一张小票订单可能被分多次支付,订单原价用于记录整张小票的交易金额。 + * 2、当订单原价与支付金额不相等,则不享受优惠。 + * 3、该字段主要用于防止同一张小票分多次支付,以享受多次优惠的情况,正常支付订单不必上传此参数。 + * 示例值:608800 + *+ */ + @SerializedName(value = "cost_price") + private Integer costPrice; + /** + *
+ * 字段名:商品小票ID + * 变量名:invoice_id + * 是否必填:否 + * 类型:string[1,32] + * 描述: + * 商品小票ID + * 示例值:微信123 + *+ */ + @SerializedName(value = "invoice_id") + private String invoiceId; + /** + *
+ * 字段名:单品列表 + * 变量名:goods_detail + * 是否必填:否 + * 类型:array + * 描述: + * 单品列表信息 + * 条目个数限制:【1,6000】 + *+ */ + @SerializedName(value = "goods_detail") + private List
+ * 字段名:商户侧商品编码 + * 变量名:merchant_goods_id + * 是否必填:是 + * 类型:string[1,32] + * 描述: + * 由半角的大小写字母、数字、中划线、下划线中的一种或几种组成。 + * 示例值:商品编码 + *+ */ + @SerializedName(value = "merchant_goods_id") + private String merchantGoodsId; + /** + *
+ * 字段名:微信侧商品编码 + * 变量名:wechatpay_goods_id + * 是否必填:否 + * 类型:string[1,32] + * 描述: + * 微信支付定义的统一商品编号(没有可不传) + * 示例值:1001 + *+ */ + @SerializedName(value = "wechatpay_goods_id") + private String wechatpayGoodsId; + /** + *
+ * 字段名:商品名称 + * 变量名:goods_name + * 是否必填:否 + * 类型:string[1,256] + * 描述: + * 商品的实际名称 + * 示例值:iPhoneX 256G + *+ */ + @SerializedName(value = "goods_name") + private String goodsName; + /** + *
+ * 字段名:商品数量 + * 变量名:quantity + * 是否必填:是 + * 类型:int + * 描述: + * 用户购买的数量 + * 示例值:1 + *+ */ + @SerializedName(value = "quantity") + private Integer quantity; + /** + *
+ * 字段名:商品单价 + * 变量名:unit_price + * 是否必填:是 + * 类型:int + * 描述: + * 商品单价,单位为分 + * 示例值:828800 + *+ */ + @SerializedName(value = "unit_price") + private Integer unitPrice; + } + + @Data + @NoArgsConstructor + public static class SceneInfo implements Serializable { + private static final long serialVersionUID = 1L; + /** + *
+ * 字段名:用户终端IP + * 变量名:payer_client_ip + * 是否必填:是 + * 类型:string[1,45] + * 描述: + * 用户的客户端IP,支持IPv4和IPv6两种格式的IP地址。 + * 示例值:14.23.150.211 + *+ */ + @SerializedName(value = "payer_client_ip") + private String payerClientIp; + /** + *
+ * 字段名:商户端设备号 + * 变量名:device_id + * 是否必填:否 + * 类型:string[1,32] + * 描述: + * 商户端设备号(门店号或收银设备ID)。 + * 示例值:013467007045764 + *+ */ + @SerializedName(value = "device_id") + private String deviceId; + /** + *
+ * 字段名:商户门店信息 + * 变量名:store_info + * 是否必填:否 + * 类型:object + * 描述: + * 商户门店信息 + *+ */ + @SerializedName(value = "store_info") + private StoreInfo storeInfo; + /** + *
+ * 字段名:H5场景信息 + * 变量名:h5_info + * 是否必填:否(H5支付必填) + * 类型:object + * 描述: + * H5场景信息 + *+ */ + @SerializedName(value = "h5_info") + private H5Info h5Info; + } + + @Data + @NoArgsConstructor + public static class StoreInfo implements Serializable { + private static final long serialVersionUID = -1L; + /** + *
+ * 字段名:门店编号 + * 变量名:id + * 是否必填:是 + * 类型:string[1,32] + * 描述: + * 商户侧门店编号 + * 示例值:0001 + *+ */ + @SerializedName(value = "id") + private String id; + /** + *
+ * 字段名:门店名称 + * 变量名:name + * 是否必填:否 + * 类型:string[1,256] + * 描述: + * 商户侧门店名称 + * 示例值:腾讯大厦分店 + *+ */ + @SerializedName(value = "name") + private String name; + /** + *
+ * 字段名:地区编码 + * 变量名:area_code + * 是否必填:否 + * 类型:string[1,32] + * 描述: + * 地区编码,详细请见省市区编号对照表(https://pay.weixin.qq.com/wiki/doc/apiv3/terms_definition/chapter1_1_3.shtml)。 + * 示例值:440305 + *+ */ + @SerializedName(value = "area_code") + private String areaCode; + /** + *
+ * 字段名:详细地址 + * 变量名:address + * 是否必填:是 + * 类型:string[1,512] + * 描述: + * 详细的商户门店地址 + * 示例值:广东省深圳市南山区科技中一道10000号 + *+ */ + @SerializedName(value = "address") + private String address; + } + + @Data + @NoArgsConstructor + public static class H5Info implements Serializable { + private static final long serialVersionUID = -1L; + /** + *
+ * 字段名:场景类型 + * 变量名:type + * 是否必填:是 + * 类型:string[1,32] + * 描述: + * 场景类型 + * 示例值:iOS, Android, Wap + *+ */ + @SerializedName(value = "type") + private String type; + /** + *
+ * 字段名:应用名称 + * 变量名:app_name + * 是否必填:否 + * 类型:string[1,64] + * 描述: + * 应用名称 + * 示例值:王者荣耀 + *+ */ + @SerializedName(value = "app_name") + private String appName; + /** + *
+ * 字段名:网站URL + * 变量名:app_url + * 是否必填:否 + * 类型:string[1,128] + * 描述: + * 网站URL + * 示例值:https://pay.qq.com + *+ */ + @SerializedName(value = "app_url") + private String appUrl; + /** + *
+ * 字段名:iOS平台BundleID + * 变量名:bundle_id + * 是否必填:否 + * 类型:string[1,128] + * 描述: + * iOS平台BundleID + * 示例值:com.tencent.wzryiOS + *+ */ + @SerializedName(value = "bundle_id") + private String bundleId; + /** + *
+ * 字段名:Android平台PackageName + * 变量名:package_name + * 是否必填:否 + * 类型:string[1,128] + * 描述: + * Android平台PackageName + * 示例值:com.tencent.tmgp.sgame + *+ */ + @SerializedName(value = "package_name") + private String packageName; + } + + @Data + @NoArgsConstructor + public static class SettleInfo implements Serializable { + private static final long serialVersionUID = 1L; + /** + *
+ * 字段名:是否指定分账 + * 变量名:profit_sharing + * 是否必填:否 + * 类型:boolean + * 描述: + * 是否指定分账 + * 示例值:false + *+ */ + @SerializedName(value = "profit_sharing") + private Boolean profitSharing; + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderQueryV3Result.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderQueryV3Result.java new file mode 100644 index 0000000000..ca3ed4c96b --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderQueryV3Result.java @@ -0,0 +1,528 @@ +package com.github.binarywang.wxpay.bean.result; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +/** + *
+ * 查询订单 返回结果对象 + *+ * + * @author thinsstar + */ +@Data +@NoArgsConstructor +public class WxPayOrderQueryV3Result implements Serializable { + private static final long serialVersionUID = 1L; + /** + *
+ * 字段名:应用ID + * 变量名:appid + * 是否必填:是 + * 类型:string[1,32] + * 描述: + * 直连商户申请的公众号或移动应用appid。 + * 示例值:wxd678efh567hg6787 + *+ */ + @SerializedName(value = "appid") + private String appid; + /** + *
+ * 字段名:直连商户号 + * 变量名:mchid + * 是否必填:是 + * 类型:string[1,32] + * 描述: + * 直连商户的商户号,由微信支付生成并下发。 + * 示例值:1230000109 + *+ */ + @SerializedName(value = "mchid") + private String mchid; + /** + *
+ * 字段名:商户订单号 + * 变量名:out_trade_no + * 是否必填:是 + * 类型:string[6,32] + * 描述: + * 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一,详见【商户订单号】。 + * 示例值:1217752501201407033233368018 + *+ */ + @SerializedName(value = "out_trade_no") + private String outTradeNo; + /** + *
+ * 字段名:微信支付订单号 + * 变量名:transaction_id + * 是否必填:否 + * 类型:string[1,32] + * 描述: + * 微信支付系统生成的订单号。 + * 示例值:1217752501201407033233368018 + *+ */ + @SerializedName(value = "transaction_id") + private String transactionId; + /** + *
+ * 字段名:交易类型 + * 变量名:trade_type + * 是否必填:否 + * 类型:string[1,16] + * 描述: + * 交易类型,枚举值: + * JSAPI:公众号支付 + * NATIVE:扫码支付 + * APP:APP支付 + * MICROPAY:付款码支付 + * MWEB:H5支付 + * FACEPAY:刷脸支付 + * 示例值:MICROPAY + *+ */ + @SerializedName(value = "trade_type") + private String tradeType; + /** + *
+ * 字段名:交易状态 + * 变量名:trade_state + * 是否必填:是 + * 类型:string[1,32] + * 描述: + * 交易状态,枚举值: + * SUCCESS:支付成功 + * REFUND:转入退款 + * NOTPAY:未支付 + * CLOSED:已关闭 + * REVOKED:已撤销(付款码支付) + * USERPAYING:用户支付中(付款码支付) + * PAYERROR:支付失败(其他原因,如银行返回失败) + * ACCEPT:已接收,等待扣款 + * 示例值:SUCCESS + *+ */ + @SerializedName(value = "trade_state") + private String tradeState; + /** + *
+ * 字段名:交易状态描述 + * 变量名:trade_state_desc + * 是否必填:是 + * 类型:string[1,256] + * 描述: + * 交易状态描述 + * 示例值:支付成功 + *+ */ + @SerializedName(value = "trade_state_desc") + private String tradeStateDesc; + /** + *
+ * 字段名:付款银行 + * 变量名:bank_type + * 是否必填:否 + * 类型:string[1,16] + * 描述: + * 银行类型,采用字符串类型的银行标识。银行标识请参考《银行类型对照表》https://pay.weixin.qq.com/wiki/doc/apiv3/terms_definition/chapter1_1_3.shtml#part-6 + * 示例值:CMC + *+ */ + @SerializedName(value = "bank_type") + private String bankType; + /** + *
+ * 字段名:附加数据 + * 变量名:attach + * 是否必填:否 + * 类型:string[1,128] + * 描述: + * 附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用 + * 示例值:自定义数据 + *+ */ + @SerializedName(value = "attach") + private String attach; + /** + *
+ * 字段名:支付完成时间 + * 变量名:success_time + * 是否必填:否 + * 类型:string[1,64] + * 描述: + * 支付完成时间,遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss表示时分秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35+08:00表示,北京时间2015年5月20日 13点29分35秒。 + * 示例值:2018-06-08T10:34:56+08:00 + *+ */ + @SerializedName(value = "success_time") + private String successTime; + /** + *
+ * 字段名:支付者 + * 变量名:payer + * 是否必填:是 + * 类型:object + * 描述: + * 支付者信息 + *+ */ + private Payer payer; + /** + *
+ * 字段名:订单金额 + * 变量名:amount + * 是否必填:否 + * 类型:object + * 描述: + * 订单金额信息,当支付成功时返回该字段。 + *+ */ + @SerializedName(value = "amount") + private Amount amount; + /** + *
+ * 字段名:场景信息 + * 变量名:scene_info + * 是否必填:否 + * 类型:object + * 描述: + * 支付场景描述 + *+ */ + @SerializedName(value = "scene_info") + private SceneInfo sceneInfo; + /** + *
+ * 字段名:优惠功能 + * 变量名:promotion_detail + * 是否必填:否 + * 类型:array + * 描述: + * 优惠功能,享受优惠时返回该字段。 + *+ */ + @SerializedName(value = "promotion_detail") + private List
+ * 字段名:用户标识 + * 变量名:openid + * 是否必填:是 + * 类型:string[1,128] + * 描述: + * 用户在直连商户appid下的唯一标识。 + * 示例值:oUpF8uMuAJO_M2pxb1Q9zNjWeS6o + *+ */ + @SerializedName(value = "openid") + private String openid; + } + + @Data + @NoArgsConstructor + public static class Amount implements Serializable { + private static final long serialVersionUID = 1L; + /** + *
+ * 字段名:总金额 + * 变量名:total + * 是否必填:否 + * 类型:int + * 描述: + * 订单总金额,单位为分。 + * 示例值:100 + *+ */ + @SerializedName(value = "total") + private Integer total; + /** + *
+ * 字段名:用户支付金额 + * 变量名:payer_total + * 是否必填:否 + * 类型:int + * 描述: + * 用户支付金额,单位为分。 + * 示例值:100 + *+ */ + @SerializedName(value = "payer_total") + private Integer payerTotal; + /** + *
+ * 字段名:货币类型 + * 变量名:currency + * 是否必填:否 + * 类型:string[1,16] + * 描述: + * CNY:人民币,境内商户号仅支持人民币。 + * 示例值:CNY + *+ */ + @SerializedName(value = "currency") + private String currency; + /** + *
+ * 字段名:用户支付币种 + * 变量名:payer_currency + * 是否必填:否 + * 类型:string[1,16] + * 描述: + * 用户支付币种 + * 示例值: CNY + *+ */ + @SerializedName(value = "payer_currency") + private String payerCurrency; + } + + @Data + @NoArgsConstructor + public static class SceneInfo implements Serializable { + private static final long serialVersionUID = 1L; + /** + *
+ * 字段名:商户端设备号 + * 变量名:device_id + * 是否必填:否 + * 类型:string[1,32] + * 描述: + * 商户端设备号(发起扣款请求的商户服务器设备号)。 + * 示例值:013467007045764 + *+ */ + @SerializedName(value = "device_id") + private String deviceId; + } + + @Data + @NoArgsConstructor + public static class PromotionDetail implements Serializable { + /** + *
+ * 字段名:券ID + * 变量名:coupon_id + * 是否必填:是 + * 类型:string[1,32] + * 描述: + * 券ID + * 示例值:109519 + *+ */ + @SerializedName(value = "coupon_id") + private String couponId; + /** + *
+ * 字段名:优惠名称 + * 变量名:name + * 是否必填:否 + * 类型:string[1,64] + * 描述: + * 优惠名称 + * 示例值:单品惠-6 + *+ */ + @SerializedName(value = "name") + private String name; + /** + *
+ * 字段名:优惠范围 + * 变量名:scope + * 是否必填:否 + * 类型:string[1,32] + * 描述: + * GLOBAL:全场代金券 + * SINGLE:单品优惠 + * 示例值:GLOBAL + *+ */ + @SerializedName(value = "scope") + private String scope; + /** + *
+ * 字段名:优惠类型 + * 变量名:type + * 是否必填:否 + * 类型:string[1,32] + * 描述: + * CASH:充值 + * NOCASH:预充值 + * 示例值:CASH + *+ */ + @SerializedName(value = "type") + private String type; + /** + *
+ * 字段名:优惠券面额 + * 变量名:amount + * 是否必填:是 + * 类型:int + * 描述: + * 优惠券面额 + * 示例值:100 + *+ */ + @SerializedName(value = "amount") + private Integer amount; + /** + *
+ * 字段名:活动ID + * 变量名:stock_id + * 是否必填:否 + * 类型:string[1,32] + * 描述: + * 活动ID + * 示例值:931386 + *+ */ + @SerializedName(value = "stock_id") + private String stockId; + /** + *
+ * 字段名:微信出资 + * 变量名:wechatpay_contribute + * 是否必填:否 + * 类型:int + * 描述: + * 微信出资,单位为分 + * 示例值:0 + *+ */ + @SerializedName(value = "wechatpay_contribute") + private Integer wechatpayContribute; + /** + *
+ * 字段名:商户出资 + * 变量名:merchant_contribute + * 是否必填:否 + * 类型:int + * 描述: + * 商户出资,单位为分 + * 示例值:0 + *+ */ + @SerializedName(value = "merchant_contribute") + private Integer merchantContribute; + /** + *
+ * 字段名:其他出资 + * 变量名:other_contribute + * 是否必填:否 + * 类型:int + * 描述: + * 其他出资,单位为分 + * 示例值:0 + *+ */ + @SerializedName(value = "other_contribute") + private Integer otherContribute; + /** + *
+ * 字段名:优惠币种 + * 变量名:currency + * 是否必填:否 + * 类型:string[1,16] + * 描述: + * CNY:人民币,境内商户号仅支持人民币。 + * 示例值:CNY + *+ */ + @SerializedName(value = "currency") + private String currency; + /** + *
+ * 字段名:单品列表 + * 变量名:goods_detail + * 是否必填:否 + * 类型:array + * 描述: + * 单品列表信息 + *+ */ + @SerializedName(value = "goods_detail") + private List
+ * 字段名:商品编码 + * 变量名:goods_id + * 是否必填:是 + * 类型:string[1,32] + * 描述: + * 商品编码 + * 示例值:M1006 + *+ */ + @SerializedName(value = "goods_id") + private String goodsId; + /** + *
+ * 字段名:商品数量 + * 变量名:quantity + * 是否必填:是 + * 类型:int + * 描述: + * 用户购买的数量 + * 示例值:1 + *+ */ + @SerializedName(value = "quantity") + private Integer quantity; + /** + *
+ * 字段名:商品单价 + * 变量名:unit_price + * 是否必填:是 + * 类型:int + * 描述: + * 商品单价,单位为分 + * 示例值:100 + *+ */ + @SerializedName(value = "unit_price") + private Integer unitPrice; + /** + *
+ * 字段名:商品优惠金额 + * 变量名:discount_amount + * 是否必填:是 + * 类型:int + * 描述: + * 商品优惠金额 + * 示例值:0 + *+ */ + @SerializedName(value = "discount_amount") + private Integer discountAmount; + /** + *
+ * 字段名:商品备注 + * 变量名:goods_remark + * 是否必填:否 + * 类型:string[1,128] + * 描述: + * 商品备注信息 + * 示例值:商品备注信息 + *+ */ + @SerializedName(value = "goods_remark") + private String goodsRemark; + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundQueryV3Result.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundQueryV3Result.java new file mode 100644 index 0000000000..7d60d9f28f --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundQueryV3Result.java @@ -0,0 +1,473 @@ +package com.github.binarywang.wxpay.bean.result; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +/** + *
+ * 微信支付-退款查询返回结果 + *+ * + * @author thinsstar + */ +@Data +@NoArgsConstructor +public class WxPayRefundQueryV3Result implements Serializable { + private static final long serialVersionUID = 1L; + /** + *
+ * 字段名:微信支付退款号 + * 变量名:refund_id + * 是否必填:是 + * 类型:string[1, 32] + * 描述: + * 微信支付退款号。 + * 示例值:50000000382019052709732678859 + *+ */ + @SerializedName(value = "refund_id") + private String refundId; + /** + *
+ * 字段名:商户退款单号 + * 变量名:out_refund_no + * 是否必填:是 + * 类型:string[1, 64] + * 描述: + * 商户系统内部的退款单号,商户系统内部唯一,只能是数字、大小写字母_-|*@ ,同一退款单号多次请求只退一笔。 + * 示例值:1217752501201407033233368018 + *+ */ + @SerializedName(value = "out_refund_no") + private String outRefundNo; + /** + *
+ * 字段名:微信支付订单号 + * 变量名:transaction_id + * 是否必填:是 + * 类型:string[1, 32] + * 描述: + * 微信支付交易订单号。 + * 示例值:1217752501201407033233368018 + *+ */ + @SerializedName(value = "transaction_id") + private String transactionId; + /** + *
+ * 字段名:商户订单号 + * 变量名:out_trade_no + * 是否必填:是 + * 类型:string[1, 32] + * 描述: + * 原支付交易对应的商户订单号。 + * 示例值:1217752501201407033233368018 + *+ */ + @SerializedName(value = "out_trade_no") + private String outTradeNo; + /** + *
+ * 字段名:退款渠道 + * 变量名:channel + * 是否必填:否 + * 类型:string[1, 16] + * 描述: + * 枚举值: + * ORIGINAL:原路退款 + * BALANCE:退回到余额 + * OTHER_BALANCE:原账户异常退到其他余额账户 + * OTHER_BANKCARD:原银行卡异常退到其他银行卡 + * 示例值:ORIGINAL + *+ */ + @SerializedName(value = "channel") + private String channel; + /** + *
+ * 字段名:退款入账账户 + * 变量名:user_received_account + * 是否必填:是 + * 类型:string[1, 64] + * 描述: + * 取当前退款单的退款入账方,有以下几种情况: + * 1)退回银行卡:{银行名称}{卡类型}{卡尾号} + * 2)退回支付用户零钱:支付用户零钱 + * 3)退还商户:商户基本账户商户结算银行账户 + * 4)退回支付用户零钱通:支付用户零钱通。 + * 示例值:招商银行信用卡0403 + *+ */ + @SerializedName(value = "user_received_account") + private String userReceivedAccount; + /** + *
+ * 字段名:退款成功时间 + * 变量名:success_time + * 是否必填:否 + * 类型:string[1, 64] + * 描述: + * 退款成功时间,当退款状态为退款成功时有返回。遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss表示时分秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35+08:00表示,北京时间2015年5月20日13点29分35秒。 + * 示例值:2020-12-01T16:18:12+08:00 + *+ */ + @SerializedName(value = "success_time") + private String successTime; + /** + *
+ * 字段名:退款创建时间 + * 变量名:create_time + * 是否必填:是 + * 类型:string[1, 64] + * 描述: + * 退款受理时间。 遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss表示时分秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35+08:00表示,北京时间2015年5月20日13点29分35秒。 + * 示例值:2020-12-01T16:18:12+08:00 + *+ */ + @SerializedName(value = "create_time") + private String createTime; + /** + *
+ * 字段名:退款状态 + * 变量名:status + * 是否必填:是 + * 类型:string[1, 32] + * 描述: + * 退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,可前往商户平台-交易中心,手动处理此笔退款。 + * 枚举值: + * SUCCESS:退款成功 + * CLOSED:退款关闭 + * PROCESSING:退款处理中 + * ABNORMAL:退款异常 + * 示例值:SUCCESS + *+ */ + @SerializedName(value = "status") + private String status; + /** + *
+ * 字段名:资金账户 + * 变量名:funds_account + * 是否必填:否 + * 类型:string[1, 32] + * 描述: + * 退款所使用资金对应的资金账户类型。 枚举值: + * UNSETTLED : 未结算资金 + * AVAILABLE : 可用余额 + * UNAVAILABLE : 不可用余额 + * OPERATION : 运营户 + * 示例值:UNSETTLED + *+ */ + @SerializedName(value = "funds_account") + private String fundsAccount; + /** + *
+ * 字段名:金额信息 + * 变量名:amount + * 是否必填:是 + * 类型:object + * 描述: + * 金额详细信息。 + *+ */ + @SerializedName(value = "amount") + private Amount amount; + /** + *
+ * 字段名:优惠退款信息 + * 变量名:promotion_detail + * 是否必填:否 + * 类型:array + * 描述: + * 优惠退款信息。 + *+ */ + public List
+ * 字段名:订单金额 + * 变量名:total + * 是否必填:是 + * 类型:int + * 描述: + * 订单总金额,单位为分。 + * 示例值:100 + *+ */ + @SerializedName(value = "total") + private Integer total; + /** + *
+ * 字段名:退款金额 + * 变量名:refund + * 是否必填:是 + * 类型:int + * 描述: + * 退款标价金额,单位为分,可以做部分退款。 + * 示例值:100 + *+ */ + @SerializedName(value = "refund") + private Integer refund; + /** + *
+ * 字段名:用户支付金额 + * 变量名:payer_total + * 是否必填:是 + * 类型:int + * 描述: + * 现金支付金额,单位为分,只能为整数。 + * 示例值:90 + *+ */ + @SerializedName(value = "payer_total") + private Integer payerTotal; + /** + *
+ * 字段名:用户退款金额 + * 变量名:payer_refund + * 是否必填:是 + * 类型:int + * 描述: + * 退款给用户的金额,不包含所有优惠券金额。 + * 示例值:90 + *+ */ + @SerializedName(value = "payer_refund") + private Integer payerRefund; + /** + *
+ * 字段名:应结退款金额 + * 变量名:settlement_refund + * 是否必填:是 + * 类型:int + * 描述: + * 去掉非充值代金券退款金额后的退款金额,单位为分,退款金额=申请退款金额-非充值代金券退款金额,退款金额<=申请退款金额。 + * 示例值:100 + *+ */ + @SerializedName(value = "settlement_refund") + private Integer settlementRefund; + /** + *
+ * 字段名:用户退款金额 + * 变量名:settlement_total + * 是否必填:是 + * 类型:int + * 描述: + * 应结订单金额=订单金额-免充值代金券金额,应结订单金额<=订单金额,单位为分。 + * 示例值:100 + *+ */ + @SerializedName(value = "settlement_total") + private Integer settlementTotal; + /** + *
+ * 字段名:优惠退款金额 + * 变量名:discount_refund + * 是否必填:否 + * 类型:int64 + * 描述: + * 优惠退款金额<=退款金额,退款金额-代金券或立减优惠退款金额为现金,说明详见代金券或立减优惠,单位为分。 + * 示例值:10 + *+ */ + @SerializedName(value = "discount_refund") + private Integer discountRefund; + /** + *
+ * 字段名:币类型 + * 变量名:currency + * 是否必填:否 + * 类型:string[1, 16] + * 描述: + * 符合ISO 4217标准的三位字母代码,目前只支持人民币:CNY。 + * 示例值:CNY + *+ */ + @SerializedName(value = "currency") + private String currency; + } + + @Data + @NoArgsConstructor + public static class PromotionDetail implements Serializable { + private static final long serialVersionUID = 1L; + /** + *
+ * 字段名:券ID + * 变量名:promotion_id + * 是否必填:是 + * 类型:string[1, 32] + * 描述: + * 券或者立减优惠id。 + * 示例值:109519 + *+ */ + @SerializedName(value = "promotion_id") + private String promotionId; + /** + *
+ * 字段名:优惠范围 + * 变量名:scope + * 是否必填:是 + * 类型:string[1, 32] + * 描述: + * 枚举值: + * GLOBAL:全场代金券 + * SINGLE:单品优惠 + * 示例值:SINGLE + *+ */ + @SerializedName(value = "scope") + private String scope; + /** + *
+ * 字段名:优惠类型 + * 变量名:type + * 是否必填:是 + * 类型:string[1, 32] + * 描述: + * 枚举值: + * COUPON:代金券,需要走结算资金的充值型代金券 + * DISCOUNT:优惠券,不走结算资金的免充值型优惠券 + * 示例值:DISCOUNT + *+ */ + @SerializedName(value = "type") + private String type; + /** + *
+ * 字段名:优惠券面额 + * 变量名:amount + * 是否必填:是 + * 类型:int + * 描述: + * 用户享受优惠的金额(优惠券面额=微信出资金额+商家出资金额+其他出资方金额 ),单位为分。 + * 示例值:5 + *+ */ + @SerializedName(value = "amount") + private Integer amount; + /** + *
+ * 字段名:优惠退款金额 + * 变量名:refund_amount + * 是否必填:是 + * 类型:int + * 描述: + * 优惠退款金额<=退款金额,退款金额-代金券或立减优惠退款金额为用户支付的现金,说明详见代金券或立减优惠,单位为分。 + * 示例值:100 + *+ */ + @SerializedName(value = "refund_amount") + private Integer refundAmount; + /** + *
+ * 字段名:商品列表 + * 变量名:goods_detail + * 是否必填:否 + * 类型:array + * 描述: + * 优惠商品发生退款时返回商品信息。 + *+ */ + @SerializedName(value = "goods_detail") + private List
+ * 字段名:商户侧商品编码 + * 变量名:merchant_goods_id + * 是否必填:是 + * 类型:string[1,32] + * 描述: + * 由半角的大小写字母、数字、中划线、下划线中的一种或几种组成。 + * 示例值:1217752501201407033233368018 + *+ */ + @SerializedName(value = "merchant_goods_id") + private String merchantGoodsId; + /** + *
+ * 字段名:微信侧商品编码 + * 变量名:wechatpay_goods_id + * 是否必填:否 + * 类型:string[1,32] + * 描述: + * 微信支付定义的统一商品编号(没有可不传)。 + * 示例值:1001 + *+ */ + @SerializedName(value = "wechatpay_goods_id") + private String wechatpayGoodsId; + /** + *
+ * 字段名:商品名称 + * 变量名:goods_name + * 是否必填:否 + * 类型:string[1,256] + * 描述: + * 商品的实际名称。 + * 示例值:iPhone6s 16G + *+ */ + @SerializedName(value = "goods_name") + private String goodsName; + /** + *
+ * 字段名:商品单价 + * 变量名:unit_price + * 是否必填:是 + * 类型:int + * 描述: + * 商品单价金额,单位为分。 + * 示例值:528800 + *+ */ + @SerializedName(value = "unit_price") + private Integer unitPrice; + /** + *
+ * 字段名:商品退款金额 + * 变量名:refund_amount + * 是否必填:是 + * 类型:int + * 描述: + * 商品退款金额,单位为分。 + * 示例值:528800 + *+ */ + @SerializedName(value = "refund_amount") + private Integer refundAmount; + /** + *
+ * 字段名:商品退货数量 + * 变量名:refund_quantity + * 是否必填:是 + * 类型:int + * 描述: + * 单品的退款数量。 + * 示例值:1 + *+ */ + @SerializedName(value = "refund_quantity") + private Integer refundQuantity; + } +} + diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundV3Result.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundV3Result.java new file mode 100644 index 0000000000..a5712f0c6d --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundV3Result.java @@ -0,0 +1,474 @@ +package com.github.binarywang.wxpay.bean.result; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +/** + *
+ * 微信支付-申请退款返回结果. + * https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_9.shtml + *+ * + * @author thinsstar + */ +@Data +@NoArgsConstructor +public class WxPayRefundV3Result implements Serializable { + private static final long serialVersionUID = -1L; + /** + *
+ * 字段名:微信退款单号 + * 变量名:refund_id + * 是否必填:是 + * 类型:string[1, 32] + * 描述: + * 微信支付退款号。 + * 示例值:50000000382019052709732678859 + *+ */ + @SerializedName(value = "refund_id") + private String refundId; + /** + *
+ * 字段名:商户退款单号 + * 变量名:out_refund_no + * 是否必填:是 + * 类型:string[1, 64] + * 描述: + * 商户系统内部的退款单号,商户系统内部唯一,只能是数字、大小写字母_-|*@ ,同一退款单号多次请求只退一笔。 + * 示例值:1217752501201407033233368018 + *+ */ + @SerializedName(value = "out_refund_no") + private String outRefundNo; + /** + *
+ * 字段名:微信支付订单号 + * 变量名:transaction_id + * 是否必填:是 + * 类型:string[1, 32] + * 描述: + * 微信支付交易订单号。 + * 示例值:1217752501201407033233368018 + *+ */ + @SerializedName(value = "transaction_id") + private String transactionId; + /** + *
+ * 字段名:商户订单号 + * 变量名:out_trade_no + * 是否必填:是 + * 类型:string[1, 32] + * 描述: + * 原支付交易对应的商户订单号。 + * 示例值:1217752501201407033233368018 + *+ */ + @SerializedName(value = "out_trade_no") + private String outTradeNo; + /** + *
+ * 字段名:退款渠道 + * 变量名:channel + * 是否必填:是 + * 类型:string[1, 32] + * 描述: + * 枚举值: + * ORIGINAL:原路退款 + * BALANCE:退回到余额 + * OTHER_BALANCE:原账户异常退到其他余额账户 + * OTHER_BANKCARD:原银行卡异常退到其他银行卡 + * 示例值:ORIGINAL + *+ */ + @SerializedName(value = "channel") + private String channel; + /** + *
+ * 字段名:退款入账账户 + * 变量名:user_received_account + * 是否必填:是 + * 类型:string[1, 64] + * 描述: + * 取当前退款单的退款入账方,有以下几种情况: + * 1)退回银行卡:{银行名称}{卡类型}{卡尾号} + * 2)退回支付用户零钱:支付用户零钱 + * 3)退还商户:商户基本账户商户结算银行账户 + * 4)退回支付用户零钱通:支付用户零钱通。 + * 示例值:招商银行信用卡0403 + *+ */ + @SerializedName(value = "user_received_account") + private String userReceivedAccount; + /** + *
+ * 字段名:退款成功时间 + * 变量名:success_time + * 是否必填:是 + * 类型:string[1, 64] + * 描述: + * 退款成功时间,当退款状态为退款成功时有返回。遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss表示时分秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35+08:00表示,北京时间2015年5月20日13点29分35秒。 + * 示例值:2020-12-01T16:18:12+08:00 + *+ */ + @SerializedName(value = "success_time") + private String successTime; + /** + *
+ * 字段名:退款创建时间 + * 变量名:create_time + * 是否必填:是 + * 类型:string[1, 64] + * 描述: + * 退款受理时间。 遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss表示时分秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35+08:00表示,北京时间2015年5月20日13点29分35秒。 + * 示例值:2020-12-01T16:18:12+08:00 + *+ */ + @SerializedName(value = "create_time") + private String createTime; + /** + *
+ * 字段名:退款状态 + * 变量名:status + * 是否必填:是 + * 类型:string[1, 32] + * 描述: + * 退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,可前往商户平台-交易中心,手动处理此笔退款。 + * 枚举值: + * SUCCESS:退款成功 + * CLOSED:退款关闭 + * PROCESSING:退款处理中 + * ABNORMAL:退款异常 + * 示例值:SUCCESS + *+ */ + @SerializedName(value = "status") + private String status; + /** + *
+ * 字段名:资金账户 + * 变量名:funds_account + * 是否必填:是 + * 类型:string[1, 32] + * 描述: + * 退款所使用资金对应的资金账户类型。 枚举值: + * UNSETTLED : 未结算资金 + * AVAILABLE : 可用余额 + * UNAVAILABLE : 不可用余额 + * OPERATION : 运营户 + * 示例值:UNSETTLED + *+ */ + @SerializedName(value = "funds_account") + private String fundsAccount; + /** + *
+ * 字段名:金额信息 + * 变量名:amount + * 是否必填:是 + * 类型:object + * 描述: + * 金额详细信息。 + *+ */ + @SerializedName(value = "amount") + private Amount amount; + /** + *
+ * 字段名:优惠退款信息 + * 变量名:promotion_detail + * 是否必填:否 + * 类型:array + * 描述: + * 优惠退款信息。 + *+ */ + @SerializedName(value = "promotion_detail") + private List
+ * 字段名:订单金额 + * 变量名:total + * 是否必填:是 + * 类型:int + * 描述: + * 订单总金额,单位为分。 + * 示例值:100 + *+ */ + @SerializedName(value = "total") + private Integer total; + /** + *
+ * 字段名:退款金额 + * 变量名:refund + * 是否必填:是 + * 类型:int + * 描述: + * 退款标价金额,单位为分,可以做部分退款。 + * 示例值:100 + *+ */ + @SerializedName(value = "refund") + private Integer refund; + /** + *
+ * 字段名:用户支付金额 + * 变量名:payer_total + * 是否必填:是 + * 类型:int + * 描述: + * 现金支付金额,单位为分,只能为整数。 + * 示例值:90 + *+ */ + @SerializedName(value = "payer_total") + private Integer payerTotal; + /** + *
+ * 字段名:用户退款金额 + * 变量名:payer_refund + * 是否必填:是 + * 类型:int + * 描述: + * 退款给用户的金额,不包含所有优惠券金额。 + * 示例值:90 + *+ */ + @SerializedName(value = "payer_refund") + private Integer payerRefund; + /** + *
+ * 字段名:应结退款金额 + * 变量名:settlement_refund + * 是否必填:是 + * 类型:int + * 描述: + * 去掉非充值代金券退款金额后的退款金额,单位为分,退款金额=申请退款金额-非充值代金券退款金额,退款金额<=申请退款金额。 + * 示例值:100 + *+ */ + @SerializedName(value = "settlement_refund") + private Integer settlementRefund; + /** + *
+ * 字段名:用户退款金额 + * 变量名:settlement_total + * 是否必填:是 + * 类型:int + * 描述: + * 应结订单金额=订单金额-免充值代金券金额,应结订单金额<=订单金额,单位为分。 + * 示例值:100 + *+ */ + @SerializedName(value = "settlement_total") + private Integer settlementTotal; + /** + *
+ * 字段名:优惠退款金额 + * 变量名:discount_refund + * 是否必填:否 + * 类型:int64 + * 描述: + * 优惠退款金额<=退款金额,退款金额-代金券或立减优惠退款金额为现金,说明详见代金券或立减优惠,单位为分。 + * 示例值:10 + *+ */ + @SerializedName(value = "discount_refund") + private Integer discountRefund; + /** + *
+ * 字段名:币类型 + * 变量名:currency + * 是否必填:否 + * 类型:string[1, 16] + * 描述: + * 符合ISO 4217标准的三位字母代码,目前只支持人民币:CNY。 + * 示例值:CNY + *+ */ + @SerializedName(value = "currency") + private String currency; + } + + @Data + @NoArgsConstructor + public static class PromotionDetail implements Serializable { + private static final long serialVersionUID = 1L; + /** + *
+ * 字段名:券ID + * 变量名:promotion_id + * 是否必填:是 + * 类型:string[1, 32] + * 描述: + * 券或者立减优惠id。 + * 示例值:109519 + *+ */ + @SerializedName(value = "promotion_id") + private String promotionId; + /** + *
+ * 字段名:优惠范围 + * 变量名:scope + * 是否必填:是 + * 类型:string[1, 32] + * 描述: + * 枚举值: + * GLOBAL:全场代金券 + * SINGLE:单品优惠 + * 示例值:SINGLE + *+ */ + @SerializedName(value = "scope") + private String scope; + /** + *
+ * 字段名:优惠类型 + * 变量名:type + * 是否必填:是 + * 类型:string[1, 32] + * 描述: + * 枚举值: + * COUPON:代金券,需要走结算资金的充值型代金券 + * DISCOUNT:优惠券,不走结算资金的免充值型优惠券 + * 示例值:DISCOUNT + *+ */ + @SerializedName(value = "type") + private String type; + /** + *
+ * 字段名:优惠券面额 + * 变量名:amount + * 是否必填:是 + * 类型:int + * 描述: + * 用户享受优惠的金额(优惠券面额=微信出资金额+商家出资金额+其他出资方金额 ),单位为分。 + * 示例值:5 + *+ */ + @SerializedName(value = "amount") + private Integer amount; + /** + *
+ * 字段名:优惠退款金额 + * 变量名:refund_amount + * 是否必填:是 + * 类型:int + * 描述: + * 优惠退款金额<=退款金额,退款金额-代金券或立减优惠退款金额为用户支付的现金,说明详见代金券或立减优惠,单位为分。 + * 示例值:100 + *+ */ + @SerializedName(value = "refund_amount") + private Integer refundAmount; + /** + *
+ * 字段名:商品列表 + * 变量名:goods_detail + * 是否必填:否 + * 类型:array + * 描述: + * 优惠商品发生退款时返回商品信息。 + *+ */ + @SerializedName(value = "goods_detail") + private List
+ * 字段名:商户侧商品编码 + * 变量名:merchant_goods_id + * 是否必填:是 + * 类型:string[1,32] + * 描述: + * 由半角的大小写字母、数字、中划线、下划线中的一种或几种组成。 + * 示例值:1217752501201407033233368018 + *+ */ + @SerializedName(value = "merchant_goods_id") + private String merchantGoodsId; + /** + *
+ * 字段名:微信侧商品编码 + * 变量名:wechatpay_goods_id + * 是否必填:否 + * 类型:string[1,32] + * 描述: + * 微信支付定义的统一商品编号(没有可不传)。 + * 示例值:1001 + *+ */ + @SerializedName(value = "wechatpay_goods_id") + private String wechatpayGoodsId; + /** + *
+ * 字段名:商品名称 + * 变量名:goods_name + * 是否必填:否 + * 类型:string[1,256] + * 描述: + * 商品的实际名称。 + * 示例值:iPhone6s 16G + *+ */ + @SerializedName(value = "goods_name") + private String goodsName; + /** + *
+ * 字段名:商品单价 + * 变量名:unit_price + * 是否必填:是 + * 类型:int + * 描述: + * 商品单价金额,单位为分。 + * 示例值:528800 + *+ */ + @SerializedName(value = "unit_price") + private Integer unitPrice; + /** + *
+ * 字段名:商品退款金额 + * 变量名:refund_amount + * 是否必填:是 + * 类型:int + * 描述: + * 商品退款金额,单位为分。 + * 示例值:528800 + *+ */ + @SerializedName(value = "refund_amount") + private Integer refundAmount; + /** + *
+ * 字段名:商品退货数量 + * 变量名:refund_quantity + * 是否必填:是 + * 类型:int + * 描述: + * 单品的退款数量。 + * 示例值:1 + *+ */ + @SerializedName(value = "refund_quantity") + private Integer refundQuantity; + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayUnifiedOrderV3Result.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayUnifiedOrderV3Result.java new file mode 100644 index 0000000000..58e74196f6 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayUnifiedOrderV3Result.java @@ -0,0 +1,124 @@ +package com.github.binarywang.wxpay.bean.result; + +import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum; +import com.github.binarywang.wxpay.v3.util.SignUtils; +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.security.PrivateKey; + +/** + *
+ * 在发起微信支付前,需要调用统一下单接口,获取"预支付交易会话标识"返回的结果 + * 参考文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_1.shtml + * https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_2_1.shtml + * https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_3_1.shtml + * https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_1.shtml + * https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_1.shtml + *+ * + * @author thinsstar + */ +@Data +@NoArgsConstructor +public class WxPayUnifiedOrderV3Result implements Serializable { + private static final long serialVersionUID = 1L; + /** + *
+ * 字段名:预支付交易会话标识(APP支付、JSAPI支付 会返回) + * 变量名:prepay_id + * 是否必填:是 + * 类型:string[1,64] + * 描述: + * 预支付交易会话标识。用于后续接口调用中使用,该值有效期为2小时 + * 示例值:wx201410272009395522657a690389285100 + *+ */ + @SerializedName("prepay_id") + private String prepayId; + + /** + *
+ * 字段名:支付跳转链接(H5支付 会返回) + * 变量名:h5_url + * 是否必填:是 + * 类型:string[1,512] + * 描述: + * h5_url为拉起微信支付收银台的中间页面,可通过访问该url来拉起微信客户端,完成支付,h5_url的有效期为5分钟。 + * 示例值:https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=wx2016121516420242444321ca0631331346&package=1405458241 + *+ */ + @SerializedName("h5_url") + private String h5Url; + + /** + *
+ * 字段名:二维码链接(NATIVE支付 会返回) + * 变量名:h5_url + * 是否必填:是 + * 类型:string[1,512] + * 描述: + * 此URL用于生成支付二维码,然后提供给用户扫码支付。 + * 注意:code_url并非固定值,使用时按照URL格式转成二维码即可。 + * 示例值:weixin://wxpay/bizpayurl/up?pr=NwY5Mz9&groupid=00 + *+ */ + @SerializedName("code_url") + private String codeUrl; + + @Data + @Accessors(chain = true) + public static class JsapiResult implements Serializable { + private String appId; + private String timeStamp; + private String nonceStr; + private String packageValue; + private String signType; + private String paySign; + + private String getSignStr() { + return String.format("%s\n%s\n%s\n%s\n", appId, timeStamp, nonceStr, packageValue); + } + } + + @Data + @Accessors(chain = true) + public static class AppResult implements Serializable { + private String appid; + private String partnerid; + private String prepayid; + private String packageValue; + private String noncestr; + private String timestamp; + + } + + public
+ * 微信基础支付v3相关服务类. + *+ * + * @author thinsstar + */ +public interface BasePayV3Service { + + /** + * 调用统一下单接口,并组装生成支付所需参数对象. + * + * @param
+ * 查询订单 + * 详见 https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_2.shtml + * 商户可以通过查询订单接口主动查询订单状态,完成下一步的业务逻辑。查询订单状态可通过微信支付订单号或商户订单号两种方式查询 + * 注意: + * 查询订单可通过微信支付订单号和商户订单号两种方式查询,两种查询方式返回结果相同 + * 需要调用查询接口的情况: + * ◆ 当商户后台、网络、服务器等出现异常,商户系统最终未接收到支付通知。 + * ◆ 调用支付接口后,返回系统错误或未知交易状态情况。 + * ◆ 调用付款码支付API,返回USERPAYING的状态。 + * ◆ 调用关单或撤销接口API之前,需确认支付状态。 + * 接口地址: + * https://api.mch.weixin.qq.com/v3/pay/transactions/id/{transaction_id} + * https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{out_trade_no} + *+ * + * @param transactionId 微信订单号 + * @param outTradeNo 商户系统内部的订单号,当没提供transactionId时需要传这个。 + * @return the wx pay order query result + * @throws WxPayException the wx pay exception + */ + WxPayOrderQueryV3Result queryOrderV3(String transactionId, String outTradeNo) throws WxPayException; + + /** + *
+ * 查询订单 + * 详见 https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_2.shtml + * 商户可以通过查询订单接口主动查询订单状态,完成下一步的业务逻辑。查询订单状态可通过微信支付订单号或商户订单号两种方式查询 + * 注意: + * 查询订单可通过微信支付订单号和商户订单号两种方式查询,两种查询方式返回结果相同 + * 需要调用查询接口的情况: + * ◆ 当商户后台、网络、服务器等出现异常,商户系统最终未接收到支付通知。 + * ◆ 调用支付接口后,返回系统错误或未知交易状态情况。 + * ◆ 调用付款码支付API,返回USERPAYING的状态。 + * ◆ 调用关单或撤销接口API之前,需确认支付状态。 + * 接口地址: + * https://api.mch.weixin.qq.com/v3/pay/transactions/id/{transaction_id} + * https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{out_trade_no} + *+ * + * @param request 查询订单请求对象 + * @return the wx pay order query result + * @throws WxPayException the wx pay exception + */ + WxPayOrderQueryV3Result queryOrderV3(WxPayOrderQueryV3Request request) throws WxPayException; + /** *
* 关闭订单. @@ -327,6 +373,40 @@ public interface WxPayService { */ WxPayOrderCloseResult closeOrder(WxPayOrderCloseRequest request) throws WxPayException; + /** + *+ * 关闭订单 + * 应用场景 + * 以下情况需要调用关单接口: + * 1、商户订单支付失败需要生成新单号重新发起支付,要对原订单号调用关单,避免重复支付; + * 2、系统下单后,用户支付超时,系统退出不再受理,避免用户继续,请调用关单接口。 + * 注意:关单没有时间限制,建议在订单生成后间隔几分钟(最短5分钟)再调用关单接口,避免出现订单状态同步不及时导致关单失败。 + * 接口地址:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_3.shtml + *+ * + * @param outTradeNo 商户系统内部的订单号 + * @return the wx pay order close result + * @throws WxPayException the wx pay exception + */ + void closeOrderV3(String outTradeNo) throws WxPayException; + + /** + *+ * 关闭订单 + * 应用场景 + * 以下情况需要调用关单接口: + * 1、商户订单支付失败需要生成新单号重新发起支付,要对原订单号调用关单,避免重复支付; + * 2、系统下单后,用户支付超时,系统退出不再受理,避免用户继续,请调用关单接口。 + * 注意:关单没有时间限制,建议在订单生成后间隔几分钟(最短5分钟)再调用关单接口,避免出现订单状态同步不及时导致关单失败。 + * 接口地址:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_3.shtml + *+ * + * @param request 关闭订单请求对象 + * @return the wx pay order close result + * @throws WxPayException the wx pay exception + */ + void closeOrderV3(WxPayOrderCloseV3Request request) throws WxPayException; + /** * 调用统一下单接口,并组装生成支付所需参数对象. * @@ -360,6 +440,25 @@ public interface WxPayService { */ WxPayUnifiedOrderResult unifiedOrder(WxPayUnifiedOrderRequest request) throws WxPayException; + /** + * 调用统一下单接口,并组装生成支付所需参数对象. + * + * @param请使用{@link com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result}里的内部类或字段 + * @param request 统一下单请求参数 + * @return 返回 {@link com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result}里的内部类或字段 + * @throws WxPayException the wx pay exception + */ + T createOrderV3(TradeTypeEnum tradeType, WxPayUnifiedOrderV3Request request) throws WxPayException; + + /** + * 在发起微信支付前,需要调用统一下单接口,获取"预支付交易会话标识" + * + * @param request 请求对象,注意一些参数如appid、mchid等不用设置,方法内会自动从配置对象中获取到(前提是对应配置中已经设置) + * @return the wx pay unified order result + * @throws WxPayException the wx pay exception + */ + WxPayUnifiedOrderV3Result unifiedOrderV3(TradeTypeEnum tradeType, WxPayUnifiedOrderV3Request request) throws WxPayException; + /** * 该接口调用“统一下单”接口,并拼装发起支付请求需要的参数. * 详见https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_5 @@ -426,6 +525,33 @@ public interface WxPayService { */ WxPayRefundResult refundV2(WxPayRefundRequest request) throws WxPayException; + /** + * + * 申请退款API(支持单品). + * 详见 https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_9.shtml + * + * 应用场景 + * 当交易发生之后一年内,由于买家或者卖家的原因需要退款时,卖家可以通过退款接口将支付金额退还给买家,微信支付将在收到退款请求并且验证成功之后,将支付款按原路退还至买家账号上。 + * + * 注意: + * 1、交易时间超过一年的订单无法提交退款 + * 2、微信支付退款支持单笔交易分多次退款(不超50次),多次退款需要提交原支付订单的商户订单号和设置不同的退款单号。申请退款总金额不能超过订单金额。 一笔退款失败后重新提交,请不要更换退款单号,请使用原商户退款单号 + * 3、错误或无效请求频率限制:6qps,即每秒钟异常或错误的退款申请请求不超过6次 + * 4、每个支付订单的部分退款次数不能超过50次 + * 5、如果同一个用户有多笔退款,建议分不同批次进行退款,避免并发退款导致退款失败 + * 6、申请退款接口的返回仅代表业务的受理情况,具体退款是否成功,需要通过退款查询接口获取结果 + * 7、一个月之前的订单申请退款频率限制为:5000/min + * + * 接口地址 + * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds + *+ * + * @param request 请求对象 + * @return 退款操作结果 wx pay refund result + * @throws WxPayException the wx pay exception + */ + WxPayRefundV3Result refundV3(WxPayRefundV3Request request) throws WxPayException; + /** ** 微信支付-查询退款. @@ -486,6 +612,36 @@ WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, Stri */ WxPayRefundQueryResult refundQueryV2(WxPayRefundQueryRequest request) throws WxPayException; + /** + *+ * 微信支付-查询退款 + * 应用场景: + * 提交退款申请后,通过调用该接口查询退款状态。退款有一定延时,建议在提交退款申请后1分钟发起查询退款状态,一般来说零钱支付的退款5分钟内到账,银行卡支付的退款1-3个工作日到账。 + * 详见 https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_10.shtml + * 接口链接:https://api.mch.weixin.qq.com/v3/refund/domestic/refunds/{out_refund_no} + *+ * + * @param outTradeNo 商户订单号 + * @return 退款信息 wx pay refund query result + * @throws WxPayException the wx pay exception + */ + WxPayRefundQueryV3Result refundQueryV3(String outTradeNo) throws WxPayException; + + /** + *+ * 微信支付-查询退款 + * 应用场景: + * 提交退款申请后,通过调用该接口查询退款状态。退款有一定延时,建议在提交退款申请后1分钟发起查询退款状态,一般来说零钱支付的退款5分钟内到账,银行卡支付的退款1-3个工作日到账。 + * 详见 https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_10.shtml + * 接口链接:https://api.mch.weixin.qq.com/v3/refund/domestic/refunds/{out_refund_no} + *+ * + * @param request 微信退款单号 + * @return 退款信息 wx pay refund query result + * @throws WxPayException the wx pay exception + */ + WxPayRefundQueryV3Result refundQueryV3(WxPayRefundQueryV3Request request) throws WxPayException; + /** * 解析支付结果通知. * 详见https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7 @@ -507,6 +663,17 @@ WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, Stri */ WxPayOrderNotifyResult parseOrderNotifyResult(String xmlData, String signType) throws WxPayException; + /** + * 解析支付结果v3通知. + * 详见https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_5.shtml + * + * @param notifyData 通知数据 + * @param header 通知头部数据,不传则表示不校验头 + * @return the wx pay order notify result + * @throws WxPayException the wx pay exception + */ + WxPayOrderNotifyV3Result parseOrderNotifyV3Result(String notifyData, SignatureHeader header) throws WxPayException; + /** * 解析退款结果通知 * 详见https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_16&index=9 @@ -517,6 +684,17 @@ WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, Stri */ WxPayRefundNotifyResult parseRefundNotifyResult(String xmlData) throws WxPayException; + /** + * 解析退款结果通知 + * 详见https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_16&index=9 + * + * @param notifyData 通知数据 + * @param header 通知头部数据,不传则表示不校验头 + * @return the wx pay refund notify result + * @throws WxPayException the wx pay exception + */ + WxPayRefundNotifyV3Result parseRefundNotifyV3Result(String notifyData, SignatureHeader header) throws WxPayException; + /** * 解析扫码支付回调通知 * 详见https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_4 diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java index d5b066036a..a369369f19 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java @@ -3,15 +3,14 @@ import com.github.binarywang.utils.qrcode.QrcodeUtils; import com.github.binarywang.wxpay.bean.WxPayApiData; import com.github.binarywang.wxpay.bean.coupon.*; -import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult; -import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult; -import com.github.binarywang.wxpay.bean.notify.WxScanPayNotifyResult; +import com.github.binarywang.wxpay.bean.notify.*; import com.github.binarywang.wxpay.bean.order.WxPayAppOrderResult; import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult; import com.github.binarywang.wxpay.bean.order.WxPayMwebOrderResult; import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult; import com.github.binarywang.wxpay.bean.request.*; import com.github.binarywang.wxpay.bean.result.*; +import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum; import com.github.binarywang.wxpay.config.WxPayConfig; import com.github.binarywang.wxpay.config.WxPayConfigHolder; import com.github.binarywang.wxpay.constant.WxPayConstants; @@ -21,9 +20,12 @@ import com.github.binarywang.wxpay.service.*; import com.github.binarywang.wxpay.util.SignUtils; import com.github.binarywang.wxpay.util.XmlConfig; +import com.github.binarywang.wxpay.v3.util.AesUtils; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import jodd.io.ZipUtil; import me.chanjar.weixin.common.error.WxRuntimeException; import org.apache.commons.lang3.StringUtils; @@ -31,10 +33,12 @@ import org.slf4j.LoggerFactory; import java.io.File; +import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.security.GeneralSecurityException; import java.util.*; import java.util.zip.ZipException; @@ -52,6 +56,7 @@ public abstract class BaseWxPayServiceImpl implements WxPayService { private static final String TOTAL_FUND_COUNT = "资金流水总笔数"; + private static final Gson GSON = new GsonBuilder().create(); final Logger log = LoggerFactory.getLogger(this.getClass()); @@ -242,6 +247,13 @@ public WxPayRefundResult refundV2(WxPayRefundRequest request) throws WxPayExcept return result; } + @Override + public WxPayRefundV3Result refundV3(WxPayRefundV3Request request) throws WxPayException { + String url = String.format("%s/v3/refund/domestic/refunds", this.getPayBaseUrl()); + String response = this.postV3(url, GSON.toJson(request)); + return GSON.fromJson(response, WxPayRefundV3Result.class); + } + @Override public WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, String outRefundNo, String refundId) throws WxPayException { @@ -278,6 +290,20 @@ public WxPayRefundQueryResult refundQueryV2(WxPayRefundQueryRequest request) thr return result; } + @Override + public WxPayRefundQueryV3Result refundQueryV3(String outTradeNo) throws WxPayException { + String url = String.format("%s/v3/refund/domestic/refunds/%s", this.getPayBaseUrl(), outTradeNo); + String response = this.getV3(url); + return GSON.fromJson(response, WxPayRefundQueryV3Result.class); + } + + @Override + public WxPayRefundQueryV3Result refundQueryV3(WxPayRefundQueryV3Request request) throws WxPayException { + String url = String.format("%s/v3/refund/domestic/refunds/%s", this.getPayBaseUrl(), request.getOutTradeNo()); + String response = this.getV3(url); + return GSON.fromJson(response, WxPayRefundQueryV3Result.class); + } + @Override public WxPayOrderNotifyResult parseOrderNotifyResult(String xmlData) throws WxPayException { return this.parseOrderNotifyResult(xmlData, null); @@ -308,6 +334,44 @@ public WxPayOrderNotifyResult parseOrderNotifyResult(String xmlData, String sign } } + /** + * 校验通知签名 + * @param header 通知头信息 + * @param data 通知数据 + * @return true:校验通过 false:校验不通过 + */ + private boolean verifyNotifySign(SignatureHeader header, String data) { + String beforeSign = String.format("%s\n%s\n%s\n", + header.getTimeStamp(), + header.getNonce(), + data); + return this.getConfig().getVerifier().verify(header.getSerial(), + beforeSign.getBytes(StandardCharsets.UTF_8), header.getSignature()); + } + + @Override + public WxPayOrderNotifyV3Result parseOrderNotifyV3Result(String notifyData, SignatureHeader header) throws WxPayException { + if (Objects.nonNull(header) && !this.verifyNotifySign(header, notifyData)) { + throw new WxPayException("非法请求,头部信息验证失败"); + } + OriginNotifyResponse response = GSON.fromJson(notifyData, OriginNotifyResponse.class); + OriginNotifyResponse.Resource resource = response.getResource(); + String cipherText = resource.getCiphertext(); + String associatedData = resource.getAssociatedData(); + String nonce = resource.getNonce(); + String apiV3Key = this.getConfig().getApiV3Key(); + try { + String result = AesUtils.decryptToString(associatedData, nonce, cipherText, apiV3Key); + WxPayOrderNotifyV3Result.DecryptNotifyResult decryptNotifyResult = GSON.fromJson(result, WxPayOrderNotifyV3Result.DecryptNotifyResult.class); + WxPayOrderNotifyV3Result notifyResult = new WxPayOrderNotifyV3Result(); + notifyResult.setRawData(response); + notifyResult.setResult(decryptNotifyResult); + return notifyResult; + } catch (GeneralSecurityException | IOException e) { + throw new WxPayException("解析报文异常!", e); + } + } + @Override public WxPayRefundNotifyResult parseRefundNotifyResult(String xmlData) throws WxPayException { try { @@ -326,6 +390,29 @@ public WxPayRefundNotifyResult parseRefundNotifyResult(String xmlData) throws Wx } } + @Override + public WxPayRefundNotifyV3Result parseRefundNotifyV3Result(String notifyData, SignatureHeader header) throws WxPayException { + if (Objects.nonNull(header) && !this.verifyNotifySign(header, notifyData)) { + throw new WxPayException("非法请求,头部信息验证失败"); + } + OriginNotifyResponse response = GSON.fromJson(notifyData, OriginNotifyResponse.class); + OriginNotifyResponse.Resource resource = response.getResource(); + String cipherText = resource.getCiphertext(); + String associatedData = resource.getAssociatedData(); + String nonce = resource.getNonce(); + String apiV3Key = this.getConfig().getApiV3Key(); + try { + String result = AesUtils.decryptToString(associatedData, nonce, cipherText, apiV3Key); + WxPayRefundNotifyV3Result.DecryptNotifyResult decryptNotifyResult = GSON.fromJson(result, WxPayRefundNotifyV3Result.DecryptNotifyResult.class); + WxPayRefundNotifyV3Result notifyResult = new WxPayRefundNotifyV3Result(); + notifyResult.setRawData(response); + notifyResult.setResult(decryptNotifyResult); + return notifyResult; + } catch (GeneralSecurityException | IOException e) { + throw new WxPayException("解析报文异常!", e); + } + } + @Override public WxScanPayNotifyResult parseScanPayNotifyResult(String xmlData, String signType) throws WxPayException { try { @@ -372,6 +459,28 @@ public WxPayOrderQueryResult queryOrder(WxPayOrderQueryRequest request) throws W return result; } + @Override + public WxPayOrderQueryV3Result queryOrderV3(String transactionId, String outTradeNo) throws WxPayException { + WxPayOrderQueryV3Request request = new WxPayOrderQueryV3Request(); + request.setOutTradeNo(StringUtils.trimToNull(outTradeNo)); + request.setTransactionId(StringUtils.trimToNull(transactionId)); + return this.queryOrderV3(request); + } + + @Override + public WxPayOrderQueryV3Result queryOrderV3(WxPayOrderQueryV3Request request) throws WxPayException { + if (StringUtils.isBlank(request.getMchid())) { + request.setMchid(this.getConfig().getMchId()); + } + String url = String.format("%s/v3/pay/transactions/out-trade-no/%s", this.getPayBaseUrl(), request.getOutTradeNo()); + if (Objects.isNull(request.getOutTradeNo())) { + url = String.format("%s/v3/pay/transactions/id/%s", this.getPayBaseUrl(), request.getTransactionId()); + } + String query = String.format("?mchid=%s", request.getMchid()); + String response = this.getV3(url + query); + return GSON.fromJson(response, WxPayOrderQueryV3Result.class); + } + @Override public WxPayOrderCloseResult closeOrder(String outTradeNo) throws WxPayException { if (StringUtils.isBlank(outTradeNo)) { @@ -396,6 +505,25 @@ public WxPayOrderCloseResult closeOrder(WxPayOrderCloseRequest request) throws W return result; } + @Override + public void closeOrderV3(String outTradeNo) throws WxPayException { + if (StringUtils.isBlank(outTradeNo)) { + throw new WxPayException("out_trade_no不能为空"); + } + WxPayOrderCloseV3Request request = new WxPayOrderCloseV3Request(); + request.setOutTradeNo(StringUtils.trimToNull(outTradeNo)); + this.closeOrderV3(request); + } + + @Override + public void closeOrderV3(WxPayOrderCloseV3Request request) throws WxPayException { + if (StringUtils.isBlank(request.getMchid())) { + request.setMchid(this.getConfig().getMchId()); + } + String url = String.format("%s/v3/pay/transactions/out-trade-no/%s/close", this.getPayBaseUrl(), request.getOutTradeNo()); + this.postV3(url, GSON.toJson(request)); + } + @Override publicT createOrder(WxPayUnifiedOrderRequest request) throws WxPayException { WxPayUnifiedOrderResult unifiedOrderResult = this.unifiedOrder(request); @@ -500,6 +628,25 @@ public WxPayUnifiedOrderResult unifiedOrder(WxPayUnifiedOrderRequest request) th return result; } + @Override + public T createOrderV3(TradeTypeEnum tradeType, WxPayUnifiedOrderV3Request request) throws WxPayException { + WxPayUnifiedOrderV3Result result = this.unifiedOrderV3(tradeType, request); + return result.getPayInfo(tradeType, request.getAppid(), request.getMchid(), this.getConfig().getPrivateKey()); + } + + @Override + public WxPayUnifiedOrderV3Result unifiedOrderV3(TradeTypeEnum tradeType, WxPayUnifiedOrderV3Request request) throws WxPayException { + if (StringUtils.isBlank(request.getAppid())) { + request.setAppid(this.getConfig().getAppId()); + } + if (StringUtils.isBlank(request.getMchid())) { + request.setMchid(this.getConfig().getMchId()); + } + String url = this.getPayBaseUrl() + tradeType.getPartnerUrl(); + String response = this.postV3(url, GSON.toJson(request)); + return GSON.fromJson(response, WxPayUnifiedOrderV3Result.class); + } + @Override @Deprecated public Map getPayInfo(WxPayUnifiedOrderRequest request) throws WxPayException { diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImplTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImplTest.java index 779916162c..e984b334fc 100644 --- a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImplTest.java +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImplTest.java @@ -9,6 +9,7 @@ import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult; import com.github.binarywang.wxpay.bean.request.*; import com.github.binarywang.wxpay.bean.result.*; +import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum; import com.github.binarywang.wxpay.constant.WxPayConstants.AccountType; import com.github.binarywang.wxpay.constant.WxPayConstants.BillType; import com.github.binarywang.wxpay.constant.WxPayConstants.SignType; @@ -18,8 +19,11 @@ import com.github.binarywang.wxpay.testbase.ApiTestModule; import com.github.binarywang.wxpay.testbase.XmlWxPayConfig; import com.github.binarywang.wxpay.util.XmlConfig; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import com.google.inject.Inject; import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.util.RandomUtils; import org.testng.annotations.DataProvider; import org.testng.annotations.Guice; import org.testng.annotations.Test; @@ -704,4 +708,71 @@ public void testQueryExchangeRate() throws WxPayException { System.out.println(result); } + private static final Gson GSON = new GsonBuilder().create(); + + @Test + public void testUnifiedOrderV3() throws WxPayException { + String outTradeNo = RandomUtils.getRandomStr(); + String notifyUrl = "https://api.qq.com/"; + System.out.println("outTradeNo = " + outTradeNo); + WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request(); + request.setOutTradeNo(outTradeNo); + request.setNotifyUrl(notifyUrl); + request.setDescription("test"); + + WxPayUnifiedOrderV3Request.Payer payer = new WxPayUnifiedOrderV3Request.Payer(); + payer.setOpenid("openid"); + request.setPayer(payer); + + //构建金额信息 + WxPayUnifiedOrderV3Request.Amount amount = new WxPayUnifiedOrderV3Request.Amount(); + //设置币种信息 + amount.setCurrency("CNY"); + //设置金额 + amount.setTotal(1); + request.setAmount(amount); + + WxPayUnifiedOrderV3Result.JsapiResult result = this.payService.createOrderV3(TradeTypeEnum.JSAPI, request); + + System.out.println(GSON.toJson(result)); + } + + @Test + public void testQueryOrderV3() throws WxPayException { + WxPayOrderQueryV3Request request = new WxPayOrderQueryV3Request(); + request.setOutTradeNo("n1ZvYqjAg3D3LUBa"); + WxPayOrderQueryV3Result result = this.payService.queryOrderV3(request); + System.out.println(GSON.toJson(result)); + } + + @Test + public void testCloseOrderV3() throws WxPayException { + WxPayOrderCloseV3Request request = new WxPayOrderCloseV3Request(); + request.setOutTradeNo("n1ZvYqjAg3D3LUBa"); + this.payService.closeOrderV3(request); + } + + @Test + public void testRefundV3() throws WxPayException { + String outRefundNo = RandomUtils.getRandomStr(); + String notifyUrl = "https://api.qq.com/"; + System.out.println("outRefundNo = " + outRefundNo); + WxPayRefundV3Request request = new WxPayRefundV3Request(); + request.setOutTradeNo("n1ZvYqjAg3D3LUBa"); + request.setOutRefundNo(outRefundNo); + request.setNotifyUrl(notifyUrl); + request.setAmount(new WxPayRefundV3Request.Amount().setRefund(100).setTotal(100).setCurrency("CNY")); + WxPayRefundV3Result result = this.payService.refundV3(request); + System.out.println(GSON.toJson(result)); + } + + @Test + public void testRefundQueryV3() throws WxPayException { + WxPayRefundQueryV3Request request = new WxPayRefundQueryV3Request(); +// request.setOutTradeNo("n1ZvYqjAg3D7LUBa"); + request.setOutTradeNo("123456789011"); + WxPayRefundQueryV3Result result = this.payService.refundQueryV3(request); + System.out.println(GSON.toJson(result)); + } + }