From 1812b233bbcccd12944aade7188f042c622cf26e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9B=BE=E6=B5=A9?= Date: Mon, 17 Aug 2020 13:32:42 +0800 Subject: [PATCH 1/8] =?UTF-8?q?art:=E8=AF=81=E4=B9=A6=E7=B1=BB=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E8=AF=BB=E5=8F=96=E4=BC=98=E5=8C=96=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../binarywang/wxpay/config/WxPayConfig.java | 131 ++++++++---------- 1 file changed, 56 insertions(+), 75 deletions(-) diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java index f5cd91c6c3..097a0fa0c7 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java @@ -115,8 +115,6 @@ public class WxPayConfig { * apiV3 证书序列号值 */ private String certSerialNo; - - /** * 微信支付分serviceId */ @@ -128,8 +126,10 @@ public class WxPayConfig { private String payScoreNotifyUrl; private CloseableHttpClient apiV3HttpClient; - - + /** + * 私钥信息 + */ + private PrivateKey privateKey; /** * p12证书文件内容的字节数组. */ @@ -197,44 +197,7 @@ public SSLContext initSSLContext() throws WxPayException { if (StringUtils.isBlank(this.getKeyPath())) { throw new WxPayException("请确保证书文件地址keyPath已配置"); } - - final String prefix = "classpath:"; - String fileHasProblemMsg = String.format(PROBLEM_MSG, this.getKeyPath()); - String fileNotFoundMsg = String.format(NOT_FOUND_MSG, this.getKeyPath()); - if (this.getKeyPath().startsWith(prefix)) { - String path = RegExUtils.removeFirst(this.getKeyPath(), prefix); - if (!path.startsWith("/")) { - path = "/" + path; - } - try { - inputStream = ResourcesUtil.getResourceAsStream(path); - if (inputStream == null) { - throw new WxPayException(fileNotFoundMsg); - } - } catch (Exception e) { - throw new WxPayException(fileNotFoundMsg, e); - } - } else if (this.getKeyPath().startsWith("http://") || this.getKeyPath().startsWith("https://")) { - try { - inputStream = new URL(this.keyPath).openStream(); - if (inputStream == null) { - throw new WxPayException(fileNotFoundMsg); - } - } catch (IOException e) { - throw new WxPayException(fileNotFoundMsg, e); - } - } else { - try { - File file = new File(this.getKeyPath()); - if (!file.exists()) { - throw new WxPayException(fileNotFoundMsg); - } - - inputStream = new FileInputStream(file); - } catch (IOException e) { - throw new WxPayException(fileHasProblemMsg, e); - } - } + inputStream = this.loadConfigInputStream(this.getKeyPath()); } try { @@ -275,39 +238,8 @@ public CloseableHttpClient initApiV3HttpClient() throws WxPayException { throw new WxPayException("请确保apiV3Key值已设置"); } - InputStream keyInputStream = null; - InputStream certInputStream = null; - final String prefix = "classpath:"; - if (privateKeyPath.startsWith(prefix)) { - String keypath = RegExUtils.removeFirst(privateKeyPath, prefix); - if (!keypath.startsWith("/")) { - keypath = "/" + keypath; - } - try { - keyInputStream = ResourcesUtil.getResourceAsStream(keypath); - if (keyInputStream == null) { - throw new WxPayException(String.format(NOT_FOUND_MSG, this.getPrivateKeyPath())); - } - } catch (Exception e) { - throw new WxPayException(String.format(NOT_FOUND_MSG, this.getPrivateKeyPath()), e); - } - } - - if (privateCertPath.startsWith(prefix)) { - String certpath = RegExUtils.removeFirst(privateCertPath, prefix); - if (!certpath.startsWith("/")) { - certpath = "/" + certpath; - } - try { - certInputStream = ResourcesUtil.getResourceAsStream(certpath); - if (certInputStream == null) { - throw new WxPayException(String.format(NOT_FOUND_MSG, this.getPrivateCertPath())); - } - } catch (Exception e) { - throw new WxPayException(String.format(NOT_FOUND_MSG, this.getPrivateCertPath()), e); - } - } - + InputStream keyInputStream = this.loadConfigInputStream(privateKeyPath); + InputStream certInputStream = this.loadConfigInputStream(privateCertPath); try { PrivateKey merchantPrivateKey = PemUtils.loadPrivateKey(keyInputStream); @@ -323,10 +255,59 @@ public CloseableHttpClient initApiV3HttpClient() throws WxPayException { .build(); this.apiV3HttpClient = httpClient; this.verifier=verifier; + this.privateKey = merchantPrivateKey; return httpClient; } catch (Exception e) { throw new WxPayException("v3请求构造异常!", e); } } + + /** + * 从配置路径 加载配置 信息(支持 classpath、本地路径、网络url) + * @param configPath 配置路径 + * @return + * @throws WxPayException + */ + private InputStream loadConfigInputStream(String configPath) throws WxPayException { + InputStream inputStream; + final String prefix = "classpath:"; + String fileHasProblemMsg = String.format(PROBLEM_MSG, configPath); + String fileNotFoundMsg = String.format(NOT_FOUND_MSG, configPath); + if (configPath.startsWith(prefix)) { + String path = RegExUtils.removeFirst(configPath, prefix); + if (!path.startsWith("/")) { + path = "/" + path; + } + try { + inputStream = ResourcesUtil.getResourceAsStream(path); + if (inputStream == null) { + throw new WxPayException(fileNotFoundMsg); + } + } catch (Exception e) { + throw new WxPayException(fileNotFoundMsg, e); + } + } else if (configPath.startsWith("http://") || configPath.startsWith("https://")) { + try { + inputStream = new URL(configPath).openStream(); + if (inputStream == null) { + throw new WxPayException(fileNotFoundMsg); + } + } catch (IOException e) { + throw new WxPayException(fileNotFoundMsg, e); + } + } else { + try { + File file = new File(configPath); + if (!file.exists()) { + throw new WxPayException(fileNotFoundMsg); + } + + inputStream = new FileInputStream(file); + } catch (IOException e) { + throw new WxPayException(fileHasProblemMsg, e); + } + } + return inputStream; + } } From 2b27ee9c9f04986060b4cd86d420eb1623a8ffa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9B=BE=E6=B5=A9?= Date: Mon, 17 Aug 2020 20:14:28 +0800 Subject: [PATCH 2/8] =?UTF-8?q?new:=E7=94=B5=E5=95=86=E6=94=B6=E4=BB=98?= =?UTF-8?q?=E9=80=9A=E4=BA=8C=E7=BA=A7=E5=95=86=E6=88=B7=E8=BF=9B=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bean/ecommerce/ApplymentsRequest.java | 796 ++++++++++++++++++ .../bean/ecommerce/ApplymentsResult.java | 43 + .../ecommerce/ApplymentsStatusResult.java | 308 +++++++ .../binarywang/wxpay/config/WxPayConfig.java | 7 +- .../wxpay/service/EcommerceService.java | 55 ++ .../wxpay/service/WxPayService.java | 6 + .../service/impl/BaseWxPayServiceImpl.java | 6 + .../service/impl/EcommerceServiceImpl.java | 52 ++ .../wxpay/v3/util/RsaCryptoUtil.java | 4 +- 9 files changed, 1272 insertions(+), 5 deletions(-) create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/ApplymentsRequest.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/ApplymentsResult.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/ApplymentsStatusResult.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/ApplymentsRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/ApplymentsRequest.java new file mode 100644 index 0000000000..92e069f631 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/ApplymentsRequest.java @@ -0,0 +1,796 @@ +package com.github.binarywang.wxpay.bean.ecommerce; + +import com.github.binarywang.wxpay.v3.SpecEncrypt; +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + + +/** + *
+ * 电商平台,可使用该接口,帮助其二级商户进件成为微信支付商户。
+ * 文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/ecommerce/applyments/chapter3_1.shtml
+ * 
+ */ +@Data +@NoArgsConstructor +public class ApplymentsRequest implements Serializable { + /** + *
+	 * 字段名:业务申请编号
+	 * 变量名:out_request_no
+	 * 是否必填:是
+	 * 类型:string(124)
+	 * 描述:
+	 *  1、服务商自定义的商户唯一编号。
+	 *  2、每个编号对应一个申请单,每个申请单审核通过后会生成一个微信支付商户号。
+	 *  3、若申请单被驳回,可填写相同的“业务申请编号”,即可覆盖修改原申请单信息 。
+	 *  示例值:APPLYMENT_00000000001
+	 * 
+ */ + @SerializedName(value = "out_request_no") + private String outRequestNo; + + /** + *
+	 * 字段名:主体类型
+	 * 变量名:organization_type
+	 * 是否必填:是
+	 * 类型:string(4)
+	 * 描述:
+	 *  枚举值:
+	 *  2401:小微商户,指无营业执照的商户。
+	 *  4:个体工商户,营业执照上的主体类型一般为个体户、个体工商户、个体经营。
+	 *  2:企业,营业执照上的主体类型一般为有限公司、有限责任公司。
+	 *  3:党政、机关及事业单位,包括国内各级、各类政府机构、事业单位等(如:公安、党团、司法、交通、旅游、工商税务、市政、医疗、教育、学校等机构)。
+	 *  1708:其他组织,不属于企业、政府/事业单位的组织机构(如社会团体、民办非企业、基金会),要求机构已办理组织机构代码证。
+	 *  示例值:2401
+	 * 
+ */ + @SerializedName(value = "organization_type") + private String organizationType; + + /** + *
+	 * 字段名:+营业执照/登记证书信息
+	 * 变量名:business_license_info
+	 * 是否必填:条件选填
+	 * 类型:object
+	 * 描述:
+	 *  1、主体为“小微”时,不填。
+	 *  2、主体为“个体工商户/企业”时,请上传营业执照。
+	 *  3、主体为“党政、机关及事业单位/其他组织”时,请上传登记证书。
+	 * 
+ */ + @SerializedName(value = "business_license_info") + private BusinessLicenseInfo businessLicenseInfo; + + /** + *
+	 * 字段名:+组织机构代码证信息
+	 * 变量名:organization_cert_info
+	 * 是否必填:条件选填
+	 * 类型:object
+	 * 描述:主体为企业/党政、机关及事业单位/其他组织,且证件号码不是18位时必填。
+	 * 
+ */ + @SerializedName(value = "organization_cert_info") + private OrganizationCertInfo organizationCertInfo; + + /** + *
+	 * 字段名:经营者/法人证件类型
+	 * 变量名:id_doc_type
+	 * 是否必填:否
+	 * 类型:string(64)
+	 * 描述:
+	 *  1、主体为“小微”,只可选择:身份证。
+	 *  2、主体为“个体户/企业/党政、机关及事业单位/其他组织”,可选择:任一证件类型。
+	 *  3、若没有填写,系统默认选择:身份证。
+	 *  枚举值:
+	 *  IDENTIFICATION_TYPE_MAINLAND_IDCARD:中国大陆居民-身份证
+	 *  IDENTIFICATION_TYPE_OVERSEA_PASSPORT:其他国家或地区居民-护照
+	 *  IDENTIFICATION_TYPE_HONGKONG:中国香港居民–来往内地通行证
+	 *  IDENTIFICATION_TYPE_MACAO:中国澳门居民–来往内地通行证
+	 *  IDENTIFICATION_TYPE_TAIWAN:中国台湾居民–来往大陆通行证
+	 *  示例值:IDENTIFICATION_TYPE_MACAO
+	 * 
+ */ + @SerializedName(value = "id_doc_type") + private String idDocType; + + /** + *
+	 * 字段名:+经营者/法人身份证信息
+	 * 变量名:id_card_info
+	 * 是否必填:条件选填
+	 * 类型:object
+	 * 描述:
+	 *  请填写经营者/法人的身份证信息
+	 *  证件类型为“身份证”时填写。
+	 *
+	 * 
+ */ + @SerializedName(value = "id_card_info") + @SpecEncrypt + private IdCardInfo idCardInfo; + + /** + *
+	 * 字段名:+经营者/法人其他类型证件信息
+	 * 变量名:id_doc_info
+	 * 是否必填:条件选填
+	 * 类型:object
+	 * 描述:证件类型为“来往内地通行证、来往大陆通行证、护照”时填写。
+	 * 
+ */ + @SerializedName(value = "id_doc_info") + private IdDocInfo idDocInfo; + + /** + *
+	 * 字段名:是否填写结算账户信息
+	 * 变量名:need_account_info
+	 * 是否必填:是
+	 * 类型:bool
+	 * 描述:
+	 *  可根据实际情况,填写“true”或“false”。
+	 *  1、若为“true”,则需填写结算账户信息。
+	 *  2、若为“false”,则无需填写结算账户信息。
+	 *  示例值:true
+	 * 
+ */ + @SerializedName(value = "need_account_info") + private Boolean needAccountInfo; + + /** + *
+	 * 字段名:+结算账户信息
+	 * 变量名:account_info
+	 * 是否必填:条件选填
+	 * 类型:object
+	 * 描述:若"是否填写结算账户信息"填写为“true”, 则必填,填写为“false”不填 。
+	 * 
+ */ + @SerializedName(value = "account_info") + @SpecEncrypt + private AccountInfo accountInfo; + + /** + *
+	 * 字段名:+超级管理员信息
+	 * 变量名:contact_info
+	 * 是否必填:是
+	 * 类型:object
+	 * 描述:
+	 *  请填写店铺的超级管理员信息。
+	 *  超级管理员需在开户后进行签约,并可接收日常重要管理信息和进行资金操作,请确定其为商户法定代表人或负责人。
+	 * 
+ */ + @SerializedName(value = "contact_info") + @SpecEncrypt + private ContactInfo contactInfo; + + /** + *
+	 * 字段名:+店铺信息
+	 * 变量名:sales_scene_info
+	 * 是否必填:是
+	 * 类型:object
+	 * 描述:请填写店铺信息
+	 * 
+ */ + @SerializedName(value = "sales_scene_info") + private SalesSceneInfo salesSceneInfo; + + /** + *
+	 * 字段名:商户简称
+	 * 变量名:merchant_shortname
+	 * 是否必填:是
+	 * 类型:string(64)
+	 * 描述:
+	 *  UTF-8格式,中文占3个字节,即最多16个汉字长度。将在支付完成页向买家展示,需与商家的实际售卖商品相符 。
+	 *  示例值:腾讯
+	 * 
+ */ + @SerializedName(value = "merchant_shortname") + private String merchantShortname; + + /** + *
+	 * 字段名:特殊资质
+	 * 变量名:qualifications
+	 * 是否必填:否
+	 * 类型:string(1024)
+	 * 描述:
+	 *  1、若店铺业务包含互联网售药,则需上传特殊资质-《互联网药品交易服务证》。
+	 *  2、最多可上传5张照片,请填写通过图片上传接口预先上传图片生成好的MediaID 。
+	 *  示例值:[\"jTpGmxUX3FBWVQ5NJInE4d2I6_H7I4\"]
+	 * 
+ */ + @SerializedName(value = "qualifications") + private String qualifications; + + /** + *
+	 * 字段名:补充材料
+	 * 变量名:business_addition_pics
+	 * 是否必填:否
+	 * 类型:string(1024)
+	 * 描述:
+	 *   最多可上传5张照片,请填写通过图片上传接口预先上传图片生成好的MediaID 。
+	 *  示例值:[\"jTpGmg05InE4d2I6_H7I4\"]
+	 * 
+ */ + @SerializedName(value = "business_addition_pics") + private String businessAdditionPics; + + /** + *
+	 * 字段名:补充说明
+	 * 变量名:business_addition_desc
+	 * 是否必填:否
+	 * 类型:string(256)
+	 * 描述:
+	 *   可填写512字以内 。
+	 *  示例值:特殊情况,说明原因
+	 * 
+ */ + @SerializedName(value = "business_addition_desc") + private String businessAdditionDesc; + + @Data + @NoArgsConstructor + public static class BusinessLicenseInfo implements Serializable{ + /** + *
+		 * 字段名:证件扫描件
+		 * 变量名:business_license_copy
+		 * 是否必填:是
+		 * 类型:string(256)
+		 * 描述:
+		 *  1、主体为“个体工商户/企业”时,请上传营业执照的证件图片。
+		 *  2、主体为“党政、机关及事业单位/其他组织”时,请上传登记证书的证件图片。
+		 *  3、可上传1张图片,请填写通过图片上传接口预先上传图片生成好的MediaID 。
+		 *  4、图片要求:
+		 *  (1)请上传证件的彩色扫描件或彩色数码拍摄件,黑白复印件需加盖公章(公章信息需完整) 。
+		 *  (2)不得添加无关水印(非微信支付商户申请用途的其他水印)。
+		 *  (3)需提供证件的正面拍摄件,完整、照面信息清晰可见。信息不清晰、扭曲、压缩变形、反光、不完整均不接受。
+		 *  (4)不接受二次剪裁、翻拍、PS的证件照片。
+		 *  示例值: 47ZC6GC-vnrbEny__Ie_An5-tCpqxucuxi-vByf3Gjm7KE53JXvGy9tqZm2XAUf-4KGprrKhpVBDIUv0OF4wFNIO4kqg05InE4d2I6_H7I4
+		 * 
+ */ + @SerializedName(value = "business_license_copy") + private String businessLicenseCopy; + + /** + *
+		 * 字段名:证件注册号
+		 * 变量名:business_license_number
+		 * 是否必填:是
+		 * 类型:string(18)
+		 * 描述:
+		 *  1、主体为“个体工商户/企业”时,请填写营业执照上的注册号/统一社会信用代码,须为15位数字或 18位数字|大写字母。
+		 *  2、主体为“党政、机关及事业单位/其他组织”时,请填写登记证书的证书编号。
+		 *  示例值:123456789012345678
+		 *  特殊规则:长度最小15个字节
+		 * 
+ */ + @SerializedName(value = "business_license_number") + private String businessLicenseNumber; + + /** + *
+		 * 字段名:商户名称
+		 * 变量名:merchant_name
+		 * 是否必填:是
+		 * 类型:string(128)
+		 * 描述:
+		 *  1、请填写营业执照/登记证书的商家名称,2~110个字符,支持括号 。
+		 *  2、个体工商户/党政、机关及事业单位,不能以“公司”结尾。
+		 *  3、个体工商户,若营业执照上商户名称为空或为“无”,请填写"个体户+经营者姓名",如“个体户张三” 。
+		 *  示例值:腾讯科技有限公司
+		 * 
+ */ + @SerializedName(value = "merchant_name") + private String merchantName; + + /** + *
+		 * 字段名:经营者/法定代表人姓名
+		 * 变量名:legal_person
+		 * 是否必填:是
+		 * 类型:string(128)
+		 * 描述:
+		 *  请填写证件的经营者/法定代表人姓名
+		 *  示例值:张三
+		 * 
+ */ + @SerializedName(value = "legal_person") + private String legalPerson; + + /** + *
+		 * 字段名:注册地址
+		 * 变量名:company_address
+		 * 是否必填:条件选填
+		 * 类型:string(128)
+		 * 描述:
+		 *  主体为“党政、机关及事业单位/其他组织”时必填,请填写登记证书的注册地址。
+		 *  示例值:深圳南山区科苑路
+		 * 
+ */ + @SerializedName(value = "company_address") + private String companyAddress; + + /** + *
+		 * 字段名:营业期限
+		 * 变量名:business_time
+		 * 是否必填:条件选填
+		 * 类型:string(256)
+		 * 描述:
+		 *  1、主体为“党政、机关及事业单位/其他组织”时必填,请填写证件有效期。
+		 *  2、若证件有效期为长期,请填写:长期。
+		 *  3、结束时间需大于开始时间。
+		 *  4、有效期必须大于60天,即结束时间距当前时间需超过60天。
+		 *  示例值:[\"2014-01-01\",\"长期\"]
+		 * 
+ */ + @SerializedName(value = "business_time") + private String businessTime; + + } + + @Data + @NoArgsConstructor + public static class OrganizationCertInfo implements Serializable { + /** + *
+		 * 字段名:组织机构代码证照片
+		 * 变量名:organization_copy
+		 * 是否必填:是
+		 * 类型:string(256)
+		 * 描述:
+		 *  可上传1张图片,请填写通过图片上传接口预先上传图片生成好的MediaID。
+		 *  示例值:vByf3Gjm7KE53JXv\prrKhpVBDIUv0OF4wFNIO4kqg05InE4d2I6_H7I4
+		 * 
+ */ + @SerializedName(value = "organization_copy") + private String organizationCopy; + + /** + *
+		 * 字段名:组织机构代码
+		 * 变量名:organization_number
+		 * 是否必填:是
+		 * 类型:string(256)
+		 * 描述:
+		 *  1、请填写组织机构代码证上的组织机构代码。
+		 *  2、可填写9或10位 数字|字母|连字符。
+		 *  示例值:12345679-A
+		 * 
+ */ + @SerializedName(value = "organization_number") + private String organizationNumber; + + /** + *
+		 * 字段名:组织机构代码有效期限
+		 * 变量名:organization_time
+		 * 是否必填:是
+		 * 类型:string(256)
+		 * 描述:
+		 *  1、请填写组织机构代码证的有效期限,注意参照示例中的格式。
+		 *  2、若证件有效期为长期,请填写:长期。
+		 *  3、结束时间需大于开始时间。
+		 *  4、有效期必须大于60天,即结束时间距当前时间需超过60天。
+		 *  示例值:[\"2014-01-01\",\"长期\"]
+		 * 
+ */ + @SerializedName(value = "organization_time") + private String organizationTime; + + } + + @Data + @NoArgsConstructor + public static class IdCardInfo implements Serializable { + /** + *
+		 * 字段名:身份证人像面照片
+		 * 变量名:id_card_copy
+		 * 是否必填:是
+		 * 类型:string(256)
+		 * 描述:
+		 *  1、请上传经营者/法定代表人的身份证人像面照片。
+		 *  2、可上传1张图片,请填写通过图片上传接口预先上传图片生成好的MediaID。
+		 *  示例值:xpnFuAxhBTEO_PvWkfSCJ3zVIn001D8daLC-ehEuo0BJqRTvDujqhThn4ReFxikqJ5YW6zFQ
+		 * 
+ */ + @SerializedName(value = "id_card_copy") + private String idCardCopy; + + /** + *
+		 * 字段名:身份证国徽面照片
+		 * 变量名:id_card_national
+		 * 是否必填:是
+		 * 类型:string(256)
+		 * 描述:
+		 *  1、请上传经营者/法定代表人的身份证国徽面照片。
+		 *  2、可上传1张图片,请填写通过图片上传接口预先上传图片生成好的MediaID 。
+		 *  示例值:vByf3Gjm7KE53JXvGy9tqZm2XAUf-4KGprrKhpVBDIUv0OF4wFNIO4kqg05InE4d2I6_H7I4
+		 * 
+ */ + @SerializedName(value = "id_card_national") + private String idCardNational; + + /** + *
+		 * 字段名:身份证姓名
+		 * 变量名:id_card_name
+		 * 是否必填:是
+		 * 类型:string(256)
+		 * 描述:
+		 *  1、请填写经营者/法定代表人对应身份证的姓名,2~30个中文字符、英文字符、符号。
+		 *  2、该字段需进行加密处理,加密方法详见敏感信息加密说明。
+		 *  示例值:pVd1HJ6v/69bDnuC4EL5Kz4jBHLiCa8MRtelw/wDa4SzfeespQO/0kjiwfqdfg==
+		 *  字段加密:使用APIv3定义的方式加密
+		 * 
+ */ + @SerializedName(value = "id_card_name") + @SpecEncrypt + private String idCardName; + + /** + *
+		 * 字段名:身份证号码
+		 * 变量名:id_card_number
+		 * 是否必填:是
+		 * 类型:string(18)
+		 * 描述:
+		 *  1、请填写经营者/法定代表人对应身份证的号码。
+		 *  2、15位数字或17位数字+1位数字|X ,该字段需进行加密处理,加密方法详见敏感信息加密说明。
+		 *  示例值:zV+BEmytMNQCqQ8juwEc4P4TG5xzchG/5IL9DBd+Z0zZXkw==4
+		 *  特殊规则:长度最小15个字节
+		 * 
+ */ + @SerializedName(value = "id_card_number") + @SpecEncrypt + private String idCardNumber; + + /** + *
+		 * 字段名:身份证有效期限
+		 * 变量名:id_card_valid_time
+		 * 是否必填:是
+		 * 类型:string(128)
+		 * 描述:
+		 *  1、请填写身份证有效期的结束时间,注意参照示例中的格式。
+		 *  2、若证件有效期为长期,请填写:长期。
+		 *  3、证件有效期需大于60天。
+		 *  示例值:2026-06-06,长期
+		 * 
+ */ + @SerializedName(value = "id_card_valid_time") + private String idCardValidTime; + + } + + @Data + @NoArgsConstructor + public static class IdDocInfo implements Serializable { + /** + *
+		 * 字段名:证件姓名
+		 * 变量名:id_doc_name
+		 * 是否必填:是
+		 * 类型:string(128)
+		 * 描述:
+		 *  请填写经营者/法人姓名。
+		 *  示例值:jTpGmxUX3FBWVQ5NJTZvlKX_gdU4LC-ehEuo0BJqRTvDujqhThn4ReFxikqJ5YW6zFQ
+		 * 
+ */ + @SerializedName(value = "id_doc_name") + private String idDocName; + + /** + *
+		 * 字段名:证件号码
+		 * 变量名:id_doc_number
+		 * 是否必填:是
+		 * 类型:string(128)
+		 * 描述:
+		 *  7~11位 数字|字母|连字符 。
+		 *  示例值:jTpGmxUX3FBWVQ5NJTZvlKX_go0BJqRTvDujqhThn4ReFxikqJ5YW6zFQ
+		 * 
+ */ + @SerializedName(value = "id_doc_number") + private String idDocNumber; + + /** + *
+		 * 字段名:证件照片
+		 * 变量名:id_doc_copy
+		 * 是否必填:是
+		 * 类型:string(256)
+		 * 描述:
+		 *  1、可上传1张图片,请填写通过图片上传接口预先上传图片生成好的MediaID。
+		 *  2、2M内的彩色图片,格式可为bmp、png、jpeg、jpg或gif 。
+		 *  示例值:xi-vByf3Gjm7KE53JXvGy9tqZm2XAUf-4KGprrKhpVBDIUv0OF4wFNIO4kqg05InE4d2I6_H7I4
+		 * 
+ */ + @SerializedName(value = "id_doc_copy") + private String idDocCopy; + + /** + *
+		 * 字段名:证件结束日期
+		 * 变量名:doc_period_end
+		 * 是否必填:是
+		 * 类型:string(128)
+		 * 描述:
+		 *  1、请按照示例值填写。
+		 *  2、若证件有效期为长期,请填写:长期。
+		 *  3、证件有效期需大于60天 。
+		 *  示例值:2020-01-02
+		 * 
+ */ + @SerializedName(value = "doc_period_end") + private String docPeriodEnd; + + } + + @Data + @NoArgsConstructor + public static class AccountInfo implements Serializable { + /** + *
+		 * 字段名:账户类型
+		 * 变量名:bank_account_type
+		 * 是否必填:是
+		 * 类型:string(2)
+		 * 描述:
+		 *  1、若主体为企业/党政、机关及事业单位/其他组织,可填写:74-对公账户。
+		 *  2、若主体为小微,可填写:75-对私账户。
+		 *  3、若主体为个体工商户,可填写:74-对公账户或75-对私账户。
+		 *  示例值:75
+		 * 
+ */ + @SerializedName(value = "bank_account_type") + private String bankAccountType; + + /** + *
+		 * 字段名:开户银行
+		 * 变量名:account_bank
+		 * 是否必填:是
+		 * 类型:string(10)
+		 * 描述:
+		 *  详细参见开户银行对照表。
+		 *  示例值:工商银行
+		 * 
+ */ + @SerializedName(value = "account_bank") + private String accountBank; + + /** + *
+		 * 字段名:开户名称
+		 * 变量名:account_name
+		 * 是否必填:是
+		 * 类型:string(128)
+		 * 描述:
+		 *  1、选择经营者个人银行卡时,开户名称必须与身份证姓名一致。
+		 *  2、选择对公账户时,开户名称必须与营业执照上的“商户名称”一致。
+		 *  3、该字段需进行加密处理,加密方法详见敏感信息加密说明。
+		 *  示例值:AOZdYGISxo4yw96uY1Pk7Rq79Jtt7+I8juwEc4P4TG5xzchG/5IL9DBd+Z0zZXkw==
+		 * 
+ */ + @SerializedName(value = "account_name") + @SpecEncrypt + private String accountName; + + /** + *
+		 * 字段名:开户银行省市编码
+		 * 变量名:bank_address_code
+		 * 是否必填:是
+		 * 类型:string(12)
+		 * 描述:
+		 *  至少精确到市,详细参见省市区编号对照表。
+		 *  示例值:110000
+		 * 
+ */ + @SerializedName(value = "bank_address_code") + private String bankAddressCode; + + /** + *
+		 * 字段名:开户银行联行号
+		 * 变量名:bank_branch_id
+		 * 是否必填:条件选填
+		 * 类型:string(64)
+		 * 描述:
+		 *  1、17家直连银行无需填写,如为其他银行,开户银行全称(含支行)和开户银行联行号二选一。
+		 *  2、详细参见开户银行全称(含支行)对照表。
+		 *  示例值:402713354941
+		 * 
+ */ + @SerializedName(value = "bank_branch_id") + private String bankBranchId; + + /** + *
+		 * 字段名:开户银行全称 (含支行)
+		 * 变量名:bank_name
+		 * 是否必填:条件选填
+		 * 类型:string(128)
+		 * 描述:
+		 *  1、17家直连银行无需填写,如为其他银行,开户银行全称(含支行)和开户银行联行号二选一。
+		 *  2、需填写银行全称,如"深圳农村商业银行XXX支行" 。
+		 *  3、详细参见开户银行全称(含支行)对照表。
+		 *  示例值:施秉县农村信用合作联社城关信用社
+		 * 
+ */ + @SerializedName(value = "bank_name") + private String bankName; + + /** + *
+		 * 字段名:银行帐号
+		 * 变量名:account_number
+		 * 是否必填:是
+		 * 类型:string(128)
+		 * 描述:
+		 *  1、数字,长度遵循系统支持的对公/对私卡号长度要求表。
+		 *  2、该字段需进行加密处理,加密方法详见敏感信息加密说明。
+		 *  示例值: d+xT+MQCvrLHUVDWv/8MR/dB7TkXLVfSrUxMPZy6jWWYzpRrEEaYQE8ZRGYoeorwC+w==
+		 * 
+ */ + @SerializedName(value = "account_number") + @SpecEncrypt + private String accountNumber; + + } + + @Data + @NoArgsConstructor + public static class ContactInfo implements Serializable { + /** + *
+		 * 字段名:超级管理员类型
+		 * 变量名:contact_type
+		 * 是否必填:是
+		 * 类型:string(2)
+		 * 描述:
+		 *  1、小微商户,选择:65-法人/经营者。
+		 *  2、个体工商户/企业/党政、机关及事业单位/其他组织,可选择:65-法人/经营者、66- 负责人。 (负责人:经商户授权办理微信支付业务的人员,授权范围包括但不限于签约,入驻过程需完成账户验证)。
+		 *  示例值:65
+		 * 
+ */ + @SerializedName(value = "contact_type") + private String contactType; + + /** + *
+		 * 字段名:超级管理员姓名
+		 * 变量名:contact_name
+		 * 是否必填:是
+		 * 类型:string(256)
+		 * 描述:
+		 *  1、若管理员类型为“法人”,则该姓名需与法人身份证姓名一致。
+		 *  2、若管理员类型为“经办人”,则可填写实际经办人的姓名。
+		 *  3、该字段需进行加密处理,加密方法详见敏感信息加密说明。
+		 *  (后续该管理员需使用实名微信号完成签约)
+		 *  示例值: pVd1HJ6zyvPedzGaV+X3IdGdbDnuC4Eelw/wDa4SzfeespQO/0kjiwfqdfg==
+		 * 
+ */ + @SerializedName(value = "contact_name") + @SpecEncrypt + private String contactName; + + /** + *
+		 * 字段名:超级管理员身份证件号码
+		 * 变量名:contact_id_card_number
+		 * 是否必填:是
+		 * 类型:string(256)
+		 * 描述:
+		 *  1、若管理员类型为法人,则该身份证号码需与法人身份证号码一致。若管理员类型为经办人,则可填写实际经办人的身份证号码。
+		 *  2、可传身份证、来往内地通行证、来往大陆通行证、护照等证件号码。
+		 *  3、超级管理员签约时,校验微信号绑定的银行卡实名信息,是否与该证件号码一致。
+		 *  4、该字段需进行加密处理,加密方法详见敏感信息加密说明。
+		 *  示例值:pVd1HJ6zmty7/mYNxLMpRSvMRtelw/wDa4SzfeespQO/0kjiwfqdfg==
+		 * 
+ */ + @SerializedName(value = "contact_id_card_number") + @SpecEncrypt + private String contactIdCardNumber; + + /** + *
+		 * 字段名:超级管理员手机
+		 * 变量名:mobile_phone
+		 * 是否必填:是
+		 * 类型:string(256)
+		 * 描述:
+		 *  1、请填写管理员的手机号,11位数字, 用于接收微信支付的重要管理信息及日常操作验证码 。
+		 *  2、该字段需进行加密处理,加密方法详见敏感信息加密说明。
+		 *  示例值:pVd1HJ6zyvPedzGaV+X3qtmrq9bb9tPROvwia4ibL+F6mfjbzQIzfb3HHLEjZ4YiNWWNeespQO/0kjiwfqdfg==
+		 * 
+ */ + @SerializedName(value = "mobile_phone") + @SpecEncrypt + private String mobilePhone; + + /** + *
+		 * 字段名:超级管理员邮箱
+		 * 变量名:contact_email
+		 * 是否必填:是
+		 * 类型:string(256)
+		 * 描述:
+		 *  1、用于接收微信支付的开户邮件及日常业务通知。
+		 *  2、需要带@,遵循邮箱格式校验 。
+		 *  3、该字段需进行加密处理,加密方法详见敏感信息加密说明。
+		 *  示例值:pVd1HJ6zyvPedzGaV+X3qtmrq9bb9tPROvwia4ibL+FWWNUlw/wDa4SzfeespQO/0kjiwfqdfg==
+		 * 
+ */ + @SerializedName(value = "contact_email") + @SpecEncrypt + private String contactEmail; + + } + + @Data + @NoArgsConstructor + public static class SalesSceneInfo implements Serializable { + /** + *
+		 * 字段名:店铺名称
+		 * 变量名:store_name
+		 * 是否必填:是
+		 * 类型:string(256)
+		 * 描述:
+		 *  请填写店铺全称。
+		 *  示例值:爱烧烤
+		 * 
+ */ + @SerializedName(value = "store_name") + private String storeName; + + /** + *
+		 * 字段名:店铺链接
+		 * 变量名:store_url
+		 * 是否必填:二选一
+		 * 类型:string(1024)
+		 * 描述:
+		 *  1、店铺二维码or店铺链接二选一必填。
+		 *  2、请填写店铺主页链接,需符合网站规范。
+		 *  示例值:http://www.qq.com
+		 * 
+ */ + @SerializedName(value = "store_url") + private String storeUrl; + + /** + *
+		 * 字段名:店铺二维码
+		 * 变量名:store_qr_code
+		 * 是否必填:1、店铺二维码 or 店铺链接二选一必填。 2、若为电商小程序,可上传店铺页面的小程序二维码。 3、请填写通过图片上传接口预先上传图片生成好的MediaID,仅能上传1张图片 。 示例值:jTpGmxUX3FBWVQ5NJTZvlKX_gdU4cRz7z5NxpnFuAxhBTEO1D8daLC-ehEuo0BJqRTvDujqhThn4ReFxikqJ5YW6zFQ
+		 * 类型:string(256)
+		 * 描述:
+		 * 
+ */ + @SerializedName(value = "store_qr_code") + private String storeQrCode; + + } + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/ApplymentsResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/ApplymentsResult.java new file mode 100644 index 0000000000..dd2d46122c --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/ApplymentsResult.java @@ -0,0 +1,43 @@ +package com.github.binarywang.wxpay.bean.ecommerce; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 二级商户进件 提交申请结果响应 + */ +@Data +@NoArgsConstructor +public class ApplymentsResult implements Serializable { + + /** + *
+   * 字段名:微信支付申请单号
+   * 变量名:applyment_id
+   * 是否必填:是
+   * 类型:uint64
+   * 描述:
+   *  微信支付分配的申请单号 。
+   *  示例值:2000002124775691
+   * 
+ */ + @SerializedName(value = "applyment_id") + private String applymentId; + + /** + *
+   * 字段名:业务申请编号
+   * 变量名:out_request_no
+   * 是否必填:是
+   * 类型:string(124)
+   * 描述:
+   *  服务商自定义的商户唯一编号。每个编号对应一个申请单,每个申请单审核通过后会生成一个微信支付商户号。
+   *  示例值:APPLYMENT_00000000001
+   * 
+ */ + @SerializedName(value = "out_request_no") + private String outRequestNo; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/ApplymentsStatusResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/ApplymentsStatusResult.java new file mode 100644 index 0000000000..00967cfa67 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/ApplymentsStatusResult.java @@ -0,0 +1,308 @@ +package com.github.binarywang.wxpay.bean.ecommerce; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +@Data +@NoArgsConstructor +public class ApplymentsStatusResult implements Serializable { + /** + *
+   * 字段名:申请状态
+   * 变量名:applyment_state
+   * 是否必填:否
+   * 类型:string(32)
+   * 描述:
+   *  枚举值:
+   *  CHECKING:资料校验中
+   *  ACCOUNT_NEED_VERIFY:待账户验证
+   *  AUDITING:审核中
+   *  REJECTED:已驳回
+   *  NEED_SIGN:待签约
+   *  FINISH:完成
+   *  FROZEN:已冻结
+   *  示例值:FINISH
+   * 
+ */ + @SerializedName(value = "applyment_state") + private String applymentState; + + /** + *
+   * 字段名:申请状态描述
+   * 变量名:applyment_state_desc
+   * 是否必填:否
+   * 类型:string(32)
+   * 描述:
+   *  申请状态描述
+   *  示例值:“审核中”
+   * 
+ */ + @SerializedName(value = "applyment_state_desc") + private String applymentStateDesc; + + /** + *
+   * 字段名:签约链接
+   * 变量名:sign_url
+   * 是否必填:否
+   * 类型:string(256)
+   * 描述:
+   *  1、当申请状态为NEED_SIGN时才返回。
+   *  2、建议将链接转为二维码展示,需让申请单-管理者用微信扫码打开,完成签约。
+   *  示例值:https://pay.weixin.qq.com/public/apply4ec_sign/s?applymentId=2000002126198476&sign=b207b673049a32c858f3aabd7d27c7ec
+   * 
+ */ + @SerializedName(value = "sign_url") + private String signUrl; + + /** + *
+   * 字段名:电商平台二级商户号
+   * 变量名:sub_mchid
+   * 是否必填:否
+   * 类型:string(32)
+   * 描述:
+   *  当申请状态为NEED_SIGN或FINISH时才返回。
+   *  示例值:1542488631
+   * 
+ */ + @SerializedName(value = "sub_mchid") + private String subMchid; + + /** + *
+   * 字段名:+汇款账户验证信息
+   * 变量名:account_validation
+   * 是否必填:否
+   * 类型:object
+   * 描述:当申请状态为ACCOUNT_NEED_VERIFY 时有返回,可根据指引汇款,完成账户验证。
+   * 
+ */ + @SerializedName(value = "account_validation") + private AccountValidation accountValidation; + + /** + *
+   * 字段名:+驳回原因详情
+   * 变量名:audit_detail
+   * 是否必填:否
+   * 类型:array
+   * 描述:各项资料的审核情况。当申请状态为REJECTED或 FROZEN时才返回。
+   * 
+ */ + @SerializedName(value = "audit_detail") + private List auditDetail; + + /** + *
+   * 字段名:法人验证链接
+   * 变量名:legal_validation_url
+   * 是否必填:否
+   * 类型:string(256)
+   * 描述:
+   *  1、当申请状态为
+   *  ACCOUNT_NEED_VERIFY,且通过系统校验的申请单,将返回链接。
+   *  2、建议将链接转为二维码展示,让商户法人用微信扫码打开,完成账户验证。
+   *  示例值: https://pay.weixin.qq.com/public/apply4ec_sign/s?applymentId=2000002126198476&sign=b207b673049a32c858f3aabd7d27c7ec
+   * 
+ */ + @SerializedName(value = "legal_validation_url") + private String legalValidationUrl; + + /** + *
+   * 字段名:业务申请编号
+   * 变量名:out_request_no
+   * 是否必填:是
+   * 类型:string(124)
+   * 描述:
+   *  提交接口填写的业务申请编号。
+   *  示例值:APPLYMENT_00000000001
+   * 
+ */ + @SerializedName(value = "out_request_no") + private String outRequestNo; + + /** + *
+   * 字段名:微信支付申请单号
+   * 变量名:applyment_id
+   * 是否必填:否
+   * 类型:uint64
+   * 描述:
+   *  微信支付分配的申请单号。
+   *  示例值:2000002124775691
+   * 
+ */ + @SerializedName(value = "applyment_id") + private String applymentId; + + @Data + @NoArgsConstructor + public static class AccountValidation implements Serializable{ + /** + *
+     * 字段名:付款户名
+     * 变量名:account_name
+     * 是否必填:否
+     * 类型:uint64
+     * 描述:
+     *  需商户使用该户名的账户进行汇款。
+     *  示例值: rDdICA3ZYXshYqeOSslSjSMf+MhhC4oaujiISFzq3AE+as7mAEDJly+DgRuVs74msmKUH8pl+3oA==
+     * 
+ */ + @SerializedName(value = "account_name") + private String accountName; + + /** + *
+     * 字段名:付款卡号
+     * 变量名:account_no
+     * 是否必填:否
+     * 类型:string(128)
+     * 描述:
+     *  结算账户为对私时会返回,商户需使用该付款卡号进行汇款。
+     *  示例值:9nZYDEvBT4rDdICA3ZYXshYqeOSslSjSauAE+as7mAEDJly+DgRuVs74msmKUH8pl+3oA==
+     * 
+ */ + @SerializedName(value = "account_no") + private String accountNo; + + /** + *
+     * 字段名:汇款金额
+     * 变量名:pay_amount
+     * 是否必填:否
+     * 类型:string(32)
+     * 描述:
+     *  需要汇款的金额(单位:分)。
+     *  示例值:124
+     * 
+ */ + @SerializedName(value = "pay_amount") + private String payAmount; + + /** + *
+     * 字段名:收款卡号
+     * 变量名:destination_account_number
+     * 是否必填:否
+     * 类型:string(128)
+     * 描述:
+     *  收款账户的卡号
+     *  示例值:7222223333322332
+     * 
+ */ + @SerializedName(value = "destination_account_number") + private String destinationAccountNumber; + + /** + *
+     * 字段名:收款户名
+     * 变量名:destination_account_name
+     * 是否必填:否
+     * 类型:string(128)
+     * 描述:
+     *  收款账户名
+     *  示例值:财付通支付科技有限公司
+     * 
+ */ + @SerializedName(value = "destination_account_name") + private String destinationAccountName; + + /** + *
+     * 字段名:开户银行
+     * 变量名:destination_account_bank
+     * 是否必填:否
+     * 类型:string(128)
+     * 描述:
+     *  收款账户的开户银行名称。
+     *  示例值:招商银行威盛大厦支行
+     * 
+ */ + @SerializedName(value = "destination_account_bank") + private String destinationAccountBank; + + /** + *
+     * 字段名:省市信息
+     * 变量名:city
+     * 是否必填:否
+     * 类型:string(128)
+     * 描述:
+     *  收款账户的省市。
+     *  示例值:深圳
+     * 
+ */ + @SerializedName(value = "city") + private String city; + + /** + *
+     * 字段名:备注信息
+     * 变量名:remark
+     * 是否必填:否
+     * 类型:string(128)
+     * 描述:
+     *  商户汇款时,需要填写的备注信息。
+     *  示例值:入驻账户验证
+     * 
+ */ + @SerializedName(value = "remark") + private String remark; + + /** + *
+     * 字段名:汇款截止时间
+     * 变量名:deadline
+     * 是否必填:否
+     * 类型:string(20)
+     * 描述:
+     *  请在此时间前完成汇款。
+     *  示例值:2018-12-1017:09:01
+     * 
+ */ + @SerializedName(value = "deadline") + private String deadline; + + } + + @Data + @NoArgsConstructor + public static class AuditDetail implements Serializable{ + /** + *
+     * 字段名:参数名称
+     * 变量名:param_name
+     * 是否必填:否
+     * 类型:string(32)
+     * 描述:
+     *  提交申请单的资料项名称。
+     *  示例值:id_card_copy
+     * 
+ */ + @SerializedName(value = "param_name") + private String paramName; + + /** + *
+     * 字段名:驳回原因
+     * 变量名:reject_reason
+     * 是否必填:否
+     * 类型:string(32)
+     * 描述:
+     *  提交资料项被驳回原因。
+     *  示例值:身份证背面识别失败,请上传更清晰的身份证图片
+     * 
+ */ + @SerializedName(value = "reject_reason") + private String rejectReason; + + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java index 097a0fa0c7..cd07619f50 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java @@ -130,6 +130,10 @@ public class WxPayConfig { * 私钥信息 */ private PrivateKey privateKey; + /** + * 证书自动更新时间差(分钟),默认一分钟 + */ + private int certAutoUpdateTime = 60; /** * p12证书文件内容的字节数组. */ @@ -245,8 +249,7 @@ public CloseableHttpClient initApiV3HttpClient() throws WxPayException { AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier( new WxPayCredentials(mchId, new PrivateKeySigner(certSerialNo, merchantPrivateKey)), - apiV3Key.getBytes(StandardCharsets.UTF_8)); - + apiV3Key.getBytes(StandardCharsets.UTF_8), this.getCertAutoUpdateTime()); CloseableHttpClient httpClient = WxPayV3HttpClientBuilder.create() .withMerchant(mchId, certSerialNo, merchantPrivateKey) diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java new file mode 100644 index 0000000000..f1bdb2b3eb --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java @@ -0,0 +1,55 @@ +package com.github.binarywang.wxpay.service; + +import com.github.binarywang.wxpay.bean.ecommerce.ApplymentsRequest; +import com.github.binarywang.wxpay.bean.ecommerce.ApplymentsResult; +import com.github.binarywang.wxpay.bean.ecommerce.ApplymentsStatusResult; +import com.github.binarywang.wxpay.exception.WxPayException; + +/** + *
+ *  电商收付通相关服务类.
+ *  接口规则:https://wechatpay-api.gitbook.io/wechatpay-api-v3
+ * 
+ * + * @author cloudX + * @date 2020/08/17 + */ +public interface EcommerceService { + /** + *
+   * 二级商户进件API
+   * 接口地址: https://api.mch.weixin.qq.com/v3/ecommerce/applyments/
+   * 文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/ecommerce/applyments/chapter3_1.shtml
+   *
+   * 
+ * + * @param request 请求对象 + * @return . + */ + ApplymentsResult createApply(ApplymentsRequest request) throws WxPayException; + + /** + *
+   * 查询申请状态API
+   * 请求URL: https://api.mch.weixin.qq.com/v3/ecommerce/applyments/{applyment_id}
+   * 文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/ecommerce/applyments/chapter3_2.shtml
+   * 
+ * + * @param applymentId 申请单ID + * @return . + */ + ApplymentsStatusResult queryApplyStatusByApplymentId(String applymentId) throws WxPayException; + + /** + *
+   * 查询申请状态API
+   * 请求URL: https://api.mch.weixin.qq.com/v3/ecommerce/applyments/out-request-no/{out_request_no}
+   * 文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/ecommerce/applyments/chapter3_2.shtml
+   * 
+ * + * @param outRequestNo 业务申请编号 + * @return . + */ + ApplymentsStatusResult queryApplyStatusByOutRequestNo(String outRequestNo) throws WxPayException; + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java index da7652ef69..2324fba170 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java @@ -126,6 +126,12 @@ public interface WxPayService { */ PayScoreService getPayScoreService(); + /** + * 获取电商收付通服务类 + * @return + */ + EcommerceService getEcommerceService(); + /** * 设置企业付款服务类,允许开发者自定义实现类. * 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 efe9e70e0a..3bd514a609 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 @@ -62,6 +62,7 @@ public abstract class BaseWxPayServiceImpl implements WxPayService { private ProfitSharingService profitSharingService = new ProfitSharingServiceImpl(this); private RedpackService redpackService = new RedpackServiceImpl(this); private PayScoreService payScoreService = new PayScoreServiceImpl(this); + private EcommerceService ecommerceService = new EcommerceServiceImpl(this); /** * The Config. @@ -88,6 +89,11 @@ public RedpackService getRedpackService() { return this.redpackService; } + @Override + public EcommerceService getEcommerceService() { + return ecommerceService; + } + @Override public void setEntPayService(EntPayService entPayService) { this.entPayService = entPayService; diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java new file mode 100644 index 0000000000..0c7e3821ba --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java @@ -0,0 +1,52 @@ +package com.github.binarywang.wxpay.service.impl; + +import com.github.binarywang.wxpay.bean.ecommerce.ApplymentsRequest; +import com.github.binarywang.wxpay.bean.ecommerce.ApplymentsResult; +import com.github.binarywang.wxpay.bean.ecommerce.ApplymentsStatusResult; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.EcommerceService; +import com.github.binarywang.wxpay.service.WxPayService; +import com.github.binarywang.wxpay.v3.util.RsaCryptoUtil; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import java.net.URI; + +public class EcommerceServiceImpl implements EcommerceService { + + private static final Gson GSON = new GsonBuilder().create(); + private WxPayService payService; + + /** + * + * @param payService + */ + public EcommerceServiceImpl(WxPayService payService) { + this.payService = payService; + } + + @Override + public ApplymentsResult createApply(ApplymentsRequest request) throws WxPayException { + String url = String.format("%s/v3/ecommerce/applyments/", this.payService.getPayBaseUrl()); + RsaCryptoUtil.encryptFields(request, this.payService.getConfig().getVerifier().getValidCertificate()); + + String result = this.payService.postV3WithWechatpaySerial(url, GSON.toJson(request)); + return GSON.fromJson(result, ApplymentsResult.class); + } + + @Override + public ApplymentsStatusResult queryApplyStatusByApplymentId(String applymentId) throws WxPayException { + String url = String.format("%s/v3/ecommerce/applyments/%s", this.payService.getPayBaseUrl(), applymentId); + String result = this.payService.getV3(URI.create(url)); + return GSON.fromJson(result, ApplymentsStatusResult.class); + } + + @Override + public ApplymentsStatusResult queryApplyStatusByOutRequestNo(String outRequestNo) throws WxPayException { + String url = String.format("%s/v3/ecommerce/applyments/out-request-no/%s", this.payService.getPayBaseUrl(), outRequestNo); + String result = this.payService.getV3(URI.create(url)); + return GSON.fromJson(result, ApplymentsStatusResult.class); + } + + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/util/RsaCryptoUtil.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/util/RsaCryptoUtil.java index b6ab923168..d88c67e419 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/util/RsaCryptoUtil.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/util/RsaCryptoUtil.java @@ -29,10 +29,8 @@ public class RsaCryptoUtil { public static void encryptFields(Object encryptObject, X509Certificate certificate) throws WxPayException { try { encryptField(encryptObject, certificate); - } catch (IllegalAccessException | IllegalBlockSizeException e) { + } catch (Exception e) { throw new WxPayException("敏感信息加密失败", e); - } catch (Exception e2) { - throw new WxPayException("敏感信息加密失败", e2); } } From 10e0ca568ee8d4efac6bf69ba5d4dd431e6759b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9B=BE=E6=B5=A9?= Date: Mon, 24 Aug 2020 20:12:34 +0800 Subject: [PATCH 3/8] =?UTF-8?q?art:=E5=BE=AE=E4=BF=A1=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E5=95=86=E9=85=8D=E7=BD=AE=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CombineTransactionsJsRequest.java | 407 ++++++++++++++++++ .../CombineTransactionsJsResult.java | 16 + .../binarywang/wxpay/config/WxPayConfig.java | 15 +- 3 files changed, 433 insertions(+), 5 deletions(-) create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsJsRequest.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsJsResult.java diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsJsRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsJsRequest.java new file mode 100644 index 0000000000..fa39f82974 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsJsRequest.java @@ -0,0 +1,407 @@ +package com.github.binarywang.wxpay.bean.ecommerce; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +/** + * 合单支付 JSAPI支付 + */ +@Data +@NoArgsConstructor +public class CombineTransactionsJsRequest implements Serializable { + /** + *
+   * 字段名:合单商户appid
+   * 变量名:combine_appid
+   * 是否必填:是
+   * 类型:string(32)
+   * 描述:
+   *   合单发起方的appid。
+   *  示例值:wxd678efh567hg6787
+   * 
+ */ + @SerializedName(value = "combine_appid") + private String combineAppid; + + /** + *
+   * 字段名:合单商户号
+   * 变量名:combine_mchid
+   * 是否必填:是
+   * 类型:string(32)
+   * 描述:
+   *  合单发起方商户号。
+   *  示例值:1900000109
+   * 
+ */ + @SerializedName(value = "combine_mchid") + private String combineMchid; + + /** + *
+   * 字段名:合单商户订单号
+   * 变量名:combine_out_trade_no
+   * 是否必填:是
+   * 类型:string(32)
+   * 描述:
+   *  合单支付总订单号,要求32个字符内,只能是数字、大小写字母_-|*@ ,且在同一个商户号下唯一。
+   *  示例值:P20150806125346
+   * 
+ */ + @SerializedName(value = "combine_out_trade_no") + private String combineOutTradeNo; + + /** + *
+   * 字段名:+场景信息
+   * 变量名:scene_info
+   * 是否必填:否
+   * 类型:object
+   * 描述:支付场景信息描述
+   * 
+ */ + @SerializedName(value = "scene_info") + private SceneInfo sceneInfo; + + /** + *
+   * 字段名:+子单信息
+   * 变量名:sub_orders
+   * 是否必填:是
+   * 类型:array
+   * 描述:
+   *  最多支持子单条数:50
+   *
+   * 
+ */ + @SerializedName(value = "sub_orders") + private List subOrders; + + /** + *
+   * 字段名:+支付者
+   * 变量名:combine_payer_info
+   * 是否必填:是
+   * 类型:object
+   * 描述:支付者信息
+   * 
+ */ + @SerializedName(value = "combine_payer_info") + private CombinePayerInfo combinePayerInfo; + + /** + *
+   * 字段名:交易起始时间
+   * 变量名:time_start
+   * 是否必填:否
+   * 类型:string(14)
+   * 描述:
+   *  订单生成时间,遵循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秒。
+   *  示例值:2019-12-31T15:59:60+08:00
+   * 
+ */ + @SerializedName(value = "time_start") + private String timeStart; + + /** + *
+   * 字段名:交易结束时间
+   * 变量名:time_expire
+   * 是否必填:否
+   * 类型:string(14)
+   * 描述:
+   *  订单失效时间,遵循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秒。
+   *  示例值:2019-12-31T15:59:60+08:00
+   * 
+ */ + @SerializedName(value = "time_expire") + private String timeExpire; + + /** + *
+   * 字段名:通知地址
+   * 变量名:notify_url
+   * 是否必填:是
+   * 类型:string(256)
+   * 描述:
+   *  接收微信支付异步通知回调地址,通知url必须为直接可访问的URL,不能携带参数。
+   *  格式: URL
+   *  示例值:https://yourapp.com/notify
+   * 
+ */ + @SerializedName(value = "notify_url") + private String notifyUrl; + + /** + *
+   * 字段名:指定支付方式
+   * 变量名:limit_pay
+   * 是否必填:否
+   * 类型:array
+   * 描述:
+   *  指定支付方式
+   *  no_credit:指定不能使用信用卡支付
+   *  特殊规则:长度最大限制32个字节
+   *  示例值:no_credit
+   * 
+ */ + @SerializedName(value = "limit_pay") + private List limitPay; + + @Data + @NoArgsConstructor + public static class SceneInfo implements Serializable{ + /** + *
+     * 字段名:商户端设备号
+     * 变量名:device_id
+     * 是否必填:否
+     * 类型:string(16)
+     * 描述:
+     *  终端设备号(门店号或收银设备ID)。
+     *  特殊规则:长度最小7个字节
+     *  示例值:POS1:1
+     * 
+ */ + @SerializedName(value = "device_id") + private String deviceId; + + /** + *
+     * 字段名:用户终端IP
+     * 变量名:payer_client_ip
+     * 是否必填:是
+     * 类型:string(45)
+     * 描述:
+     *  用户端实际ip
+     *  格式: ip(ipv4+ipv6)
+     *  示例值:14.17.22.32
+     * 
+ */ + @SerializedName(value = "payer_client_ip") + private String payerClientIp; + + } + + @Data + @NoArgsConstructor + public static class SubOrders implements Serializable{ + /** + *
+     * 字段名:子单商户号
+     * 变量名:mchid
+     * 是否必填:是
+     * 类型:string(32)
+     * 描述:
+     *  子单发起方商户号,必须与发起方appid有绑定关系。
+     *  示例值:1900000109
+     *  此处一般填写服务商商户号
+     * 
+ */ + @SerializedName(value = "mchid") + private String mchid; + + /** + *
+     * 字段名:附加信息
+     * 变量名:attach
+     * 是否必填:是
+     * 类型:string(128)
+     * 描述:
+     *  附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用。
+     *  示例值:深圳分店
+     * 
+ */ + @SerializedName(value = "attach") + private String attach; + + /** + *
+     * 字段名:+订单金额
+     * 变量名:amount
+     * 是否必填:是
+     * 类型:object
+     * 描述:
+     * 
+ */ + @SerializedName(value = "amount") + private Amount amount; + + /** + *
+     * 字段名:子单商户订单号
+     * 变量名:out_trade_no
+     * 是否必填:是
+     * 类型:string(32)
+     * 描述:
+     *  商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*@ ,且在同一个商户号下唯一。
+     *  特殊规则:最小字符长度为6
+     *  示例值:20150806125346
+     * 
+ */ + @SerializedName(value = "out_trade_no") + private String outTradeNo; + + /** + *
+     * 字段名:二级商户号
+     * 变量名:sub_mchid
+     * 是否必填:是
+     * 类型:string(32)
+     * 描述:
+     *  二级商户商户号,由微信支付生成并下发。
+     *  注意:仅适用于电商平台 服务商
+     *  示例值:1900000109
+     * 
+ */ + @SerializedName(value = "sub_mchid") + private String subMchid; + + /** + *
+     * 字段名:商品详情
+     * 变量名:detail
+     * 是否必填:否
+     * 类型:string(6000)
+     * 描述:商品详细描述(商品列表)
+     * 
+ */ + @SerializedName(value = "detail") + private String detail; + + /** + *
+     * 字段名:是否指定分账
+     * 变量名:profit_sharing
+     * 是否必填:是
+     * 类型:bool
+     * 描述:
+     *  是否指定分账
+     *  true:是
+     *  false:否
+     *  示例值:true
+     * 
+ */ + @SerializedName(value = "profit_sharing") + private Boolean profitSharing; + + /** + *
+     * 字段名:商品描述
+     * 变量名:description
+     * 是否必填:是
+     * 类型:string(128)
+     * 描述:
+     *  商品简单描述。需传入应用市场上的APP名字-实际商品名称,例如:天天爱消除-游戏充值。
+     *  示例值:腾讯充值中心-QQ会员充值
+     * 
+ */ + @SerializedName(value = "description") + private String description; + + /** + *
+     * 字段名:+结算信息
+     * 变量名:settle_info
+     * 是否必填:否
+     * 类型:Object
+     * 描述:结算信息
+     * 
+ */ + @SerializedName(value = "settle_info") + private SettleInfo settleInfo; + + } + + @Data + @NoArgsConstructor + public static class CombinePayerInfo implements Serializable{ + /** + *
+     * 字段名:用户标识
+     * 变量名:openid
+     * 是否必填:是
+     * 类型:string(128)
+     * 描述:
+     *  使用合单appid获取的对应用户openid。是用户在商户appid下的唯一标识。
+     *  示例值:oUpF8uMuAJO_M2pxb1Q9zNjWeS6o
+     * 
+ */ + @SerializedName(value = "openid") + private String openid; + + } + + @Data + @NoArgsConstructor + public static class Amount implements Serializable { + /** + *
+     * 字段名:标价金额
+     * 变量名:total_amount
+     * 是否必填:是
+     * 类型:int64
+     * 描述:
+     *  子单金额,单位为分。
+     *  示例值:100
+     * 
+ */ + @SerializedName(value = "total_amount") + private Integer totalAmount; + + /** + *
+     * 字段名:标价币种
+     * 变量名:currency
+     * 是否必填:是
+     * 类型:string(8)
+     * 描述:
+     *  符合ISO 4217标准的三位字母代码,人民币:CNY。
+     *  示例值:CNY
+     * 
+ */ + @SerializedName(value = "currency") + private String currency; + + } + + @Data + @NoArgsConstructor + public static class SettleInfo implements Serializable{ + /** + *
+     * 字段名:是否指定分账
+     * 变量名:profit_sharing
+     * 是否必填:否
+     * 类型:bool
+     * 描述:
+     *  是否分账,与外层profit_sharing同时存在时,以本字段为准。
+     *  true:是
+     *  false:否
+     *  示例值:true
+     * 
+ */ + @SerializedName(value = "profit_sharing") + private Boolean profitSharing; + + /** + *
+     * 字段名:补差金额
+     * 变量名:subsidy_amount
+     * 是否必填:否
+     * 类型:int64
+     * 描述:
+     *  SettleInfo.profit_sharing为true时,该金额才生效。
+     *  示例值:10
+     * 
+ */ + @SerializedName(value = "subsidy_amount") + private Integer subsidyAmount; + + } + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsJsResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsJsResult.java new file mode 100644 index 0000000000..f8f826cbea --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsJsResult.java @@ -0,0 +1,16 @@ +package com.github.binarywang.wxpay.bean.ecommerce; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +@Data +@NoArgsConstructor +public class CombineTransactionsJsResult implements Serializable { + + @SerializedName("prepay_id") + private String prepayId; + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java index 2769e22018..1f012ed2f7 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java @@ -19,6 +19,7 @@ import java.nio.charset.StandardCharsets; import java.security.KeyStore; import java.security.PrivateKey; +import java.security.cert.X509Certificate; import java.util.Collections; /** @@ -229,7 +230,7 @@ public SSLContext initSSLContext() throws WxPayException { public CloseableHttpClient initApiV3HttpClient() throws WxPayException { String privateKeyPath = this.getPrivateKeyPath(); String privateCertPath = this.getPrivateCertPath(); - String certSerialNo = this.getCertSerialNo(); + String serialNo = this.getCertSerialNo(); String apiV3Key = this.getApiV3Key(); if (StringUtils.isBlank(privateKeyPath)) { throw new WxPayException("请确保privateKeyPath已设置"); @@ -237,9 +238,9 @@ public CloseableHttpClient initApiV3HttpClient() throws WxPayException { if (StringUtils.isBlank(privateCertPath)) { throw new WxPayException("请确保privateCertPath已设置"); } - if (StringUtils.isBlank(certSerialNo)) { - throw new WxPayException("请确保certSerialNo证书序列号已设置"); - } +// if (StringUtils.isBlank(certSerialNo)) { +// throw new WxPayException("请确保certSerialNo证书序列号已设置"); +// } if (StringUtils.isBlank(apiV3Key)) { throw new WxPayException("请确保apiV3Key值已设置"); } @@ -248,6 +249,10 @@ public CloseableHttpClient initApiV3HttpClient() throws WxPayException { InputStream certInputStream = this.loadConfigInputStream(privateCertPath); try { PrivateKey merchantPrivateKey = PemUtils.loadPrivateKey(keyInputStream); + X509Certificate certificate = PemUtils.loadCertificate(certInputStream); + if(StringUtils.isBlank(serialNo)){ + this.certSerialNo = certificate.getSerialNumber().toString(16).toUpperCase(); + } AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier( new WxPayCredentials(mchId, new PrivateKeySigner(certSerialNo, merchantPrivateKey)), @@ -255,7 +260,7 @@ public CloseableHttpClient initApiV3HttpClient() throws WxPayException { CloseableHttpClient httpClient = WxPayV3HttpClientBuilder.create() .withMerchant(mchId, certSerialNo, merchantPrivateKey) - .withWechatpay(Collections.singletonList(PemUtils.loadCertificate(certInputStream))) + .withWechatpay(Collections.singletonList(certificate)) .withValidator(new WxPayValidator(verifier)) .build(); this.apiV3HttpClient = httpClient; From 5db9706ba0d669b89dee6293ff92a696ac317063 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9B=BE=E6=B5=A9?= Date: Mon, 24 Aug 2020 20:21:07 +0800 Subject: [PATCH 4/8] =?UTF-8?q?new:jsapi=E5=90=88=E5=8D=95=E6=94=AF?= =?UTF-8?q?=E4=BB=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bean/ecommerce/ApplymentsStatusResult.java | 4 ++++ .../ecommerce/CombineTransactionsJsResult.java | 14 ++++++++++++++ .../wxpay/service/EcommerceService.java | 15 ++++++++++++--- .../wxpay/service/impl/EcommerceServiceImpl.java | 11 ++++++++--- 4 files changed, 38 insertions(+), 6 deletions(-) diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/ApplymentsStatusResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/ApplymentsStatusResult.java index 7defd21452..b3704d6526 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/ApplymentsStatusResult.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/ApplymentsStatusResult.java @@ -7,6 +7,10 @@ import java.io.Serializable; import java.util.List; +/** + * 二级商户进件 查询申请状态结果响应 + * + */ @Data @NoArgsConstructor public class ApplymentsStatusResult implements Serializable { diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsJsResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsJsResult.java index f8f826cbea..7a33ec8404 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsJsResult.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsJsResult.java @@ -6,10 +6,24 @@ import java.io.Serializable; +/** + * 合单支付 JSAPI支付结果响应 + */ @Data @NoArgsConstructor public class CombineTransactionsJsResult implements Serializable { + /** + *
+   * 字段名:预支付交易会话标识
+   * 变量名:prepay_id
+   * 是否必填:是
+   * 类型:string(64)
+   * 描述:
+   *  数字和字母。微信生成的预支付会话标识,用于后续接口调用使用。
+   *  示例值:wx201410272009395522657a690389285100
+   * 
+ */ @SerializedName("prepay_id") private String prepayId; diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java index 5c86306b9f..066fe372cb 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java @@ -1,8 +1,6 @@ package com.github.binarywang.wxpay.service; -import com.github.binarywang.wxpay.bean.ecommerce.ApplymentsRequest; -import com.github.binarywang.wxpay.bean.ecommerce.ApplymentsResult; -import com.github.binarywang.wxpay.bean.ecommerce.ApplymentsStatusResult; +import com.github.binarywang.wxpay.bean.ecommerce.*; import com.github.binarywang.wxpay.exception.WxPayException; /** @@ -55,4 +53,15 @@ public interface EcommerceService { */ ApplymentsStatusResult queryApplyStatusByOutRequestNo(String outRequestNo) throws WxPayException; + /** + *
+   * 合单下单-JS支付API.
+   * 请求URL:https://api.mch.weixin.qq.com/v3/combine-transactions/jsapi
+   * 文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/combine/chapter3_2.shtml
+   * 
+ * + * @param request 请求对象 + * @return 预支付交易会话标识, 数字和字母。微信生成的预支付会话标识,用于后续接口调用使用。 + */ + CombineTransactionsJsResult combineTransactions(CombineTransactionsJsRequest request) throws WxPayException; } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java index 9631631272..fe916819e7 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java @@ -1,8 +1,6 @@ package com.github.binarywang.wxpay.service.impl; -import com.github.binarywang.wxpay.bean.ecommerce.ApplymentsRequest; -import com.github.binarywang.wxpay.bean.ecommerce.ApplymentsResult; -import com.github.binarywang.wxpay.bean.ecommerce.ApplymentsStatusResult; +import com.github.binarywang.wxpay.bean.ecommerce.*; import com.github.binarywang.wxpay.exception.WxPayException; import com.github.binarywang.wxpay.service.EcommerceService; import com.github.binarywang.wxpay.service.WxPayService; @@ -41,5 +39,12 @@ public ApplymentsStatusResult queryApplyStatusByOutRequestNo(String outRequestNo return GSON.fromJson(result, ApplymentsStatusResult.class); } + @Override + public CombineTransactionsJsResult combineTransactions(CombineTransactionsJsRequest request) throws WxPayException { + String url = String.format("%s/v3/combine-transactions/jsapi", this.payService.getPayBaseUrl()); + String response = this.payService.postV3(url, GSON.toJson(request)); + return GSON.fromJson(response, CombineTransactionsJsResult.class); + } + } From e0d87ebe49e34a36e0d95a804452d4a223586cd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9B=BE=E6=B5=A9?= Date: Wed, 26 Aug 2020 12:17:30 +0800 Subject: [PATCH 5/8] =?UTF-8?q?new:=E5=90=88=E5=8D=95=E6=94=AF=E4=BB=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CombineTransactionsJsResult.java | 30 ---- ...t.java => CombineTransactionsRequest.java} | 146 ++++++++++++------ .../ecommerce/CombineTransactionsResult.java | 119 ++++++++++++++ .../bean/ecommerce/enums/TradeTypeEnum.java | 27 ++++ .../wxpay/service/EcommerceService.java | 4 +- .../service/impl/EcommerceServiceImpl.java | 10 +- .../binarywang/wxpay/v3/util/SignUtils.java | 47 ++++++ 7 files changed, 300 insertions(+), 83 deletions(-) delete mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsJsResult.java rename weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/{CombineTransactionsJsRequest.java => CombineTransactionsRequest.java} (81%) create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsResult.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/enums/TradeTypeEnum.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/util/SignUtils.java diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsJsResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsJsResult.java deleted file mode 100644 index 7a33ec8404..0000000000 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsJsResult.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.github.binarywang.wxpay.bean.ecommerce; - -import com.google.gson.annotations.SerializedName; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.io.Serializable; - -/** - * 合单支付 JSAPI支付结果响应 - */ -@Data -@NoArgsConstructor -public class CombineTransactionsJsResult implements Serializable { - - /** - *
-   * 字段名:预支付交易会话标识
-   * 变量名:prepay_id
-   * 是否必填:是
-   * 类型:string(64)
-   * 描述:
-   *  数字和字母。微信生成的预支付会话标识,用于后续接口调用使用。
-   *  示例值:wx201410272009395522657a690389285100
-   * 
- */ - @SerializedName("prepay_id") - private String prepayId; - -} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsJsRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsRequest.java similarity index 81% rename from weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsJsRequest.java rename to weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsRequest.java index fa39f82974..3b138b4882 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsJsRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsRequest.java @@ -8,11 +8,11 @@ import java.util.List; /** - * 合单支付 JSAPI支付 + * 合单支付 对象 */ @Data @NoArgsConstructor -public class CombineTransactionsJsRequest implements Serializable { +public class CombineTransactionsRequest implements Serializable { /** *
    * 字段名:合单商户appid
@@ -85,7 +85,7 @@ public class CombineTransactionsJsRequest implements Serializable {
    * 
    * 字段名:+支付者
    * 变量名:combine_payer_info
-   * 是否必填:是
+   * 是否必填:否(JSAPI必填)
    * 类型:object
    * 描述:支付者信息
    * 
@@ -136,25 +136,10 @@ public class CombineTransactionsJsRequest implements Serializable { @SerializedName(value = "notify_url") private String notifyUrl; - /** - *
-   * 字段名:指定支付方式
-   * 变量名:limit_pay
-   * 是否必填:否
-   * 类型:array
-   * 描述:
-   *  指定支付方式
-   *  no_credit:指定不能使用信用卡支付
-   *  特殊规则:长度最大限制32个字节
-   *  示例值:no_credit
-   * 
- */ - @SerializedName(value = "limit_pay") - private List limitPay; @Data @NoArgsConstructor - public static class SceneInfo implements Serializable{ + public static class SceneInfo implements Serializable { /** *
      * 字段名:商户端设备号
@@ -185,11 +170,23 @@ public static class SceneInfo implements Serializable{
     @SerializedName(value = "payer_client_ip")
     private String payerClientIp;
 
+    /**
+     * 
+     * 字段名:H5场景信息
+     * 变量名:h5_info
+     * 是否必填:否(H5支付必填)
+     * 类型:object
+     * 描述:
+     *  H5场景信息
+     * 
+ */ + @SerializedName(value = "h5_info") + private H5Info h5Info; } @Data @NoArgsConstructor - public static class SubOrders implements Serializable{ + public static class SubOrders implements Serializable { /** *
      * 字段名:子单商户号
@@ -261,34 +258,6 @@ public static class SubOrders implements Serializable{
     @SerializedName(value = "sub_mchid")
     private String subMchid;
 
-    /**
-     * 
-     * 字段名:商品详情
-     * 变量名:detail
-     * 是否必填:否
-     * 类型:string(6000)
-     * 描述:商品详细描述(商品列表)
-     * 
- */ - @SerializedName(value = "detail") - private String detail; - - /** - *
-     * 字段名:是否指定分账
-     * 变量名:profit_sharing
-     * 是否必填:是
-     * 类型:bool
-     * 描述:
-     *  是否指定分账
-     *  true:是
-     *  false:否
-     *  示例值:true
-     * 
- */ - @SerializedName(value = "profit_sharing") - private Boolean profitSharing; - /** *
      * 字段名:商品描述
@@ -319,7 +288,7 @@ public static class SubOrders implements Serializable{
 
   @Data
   @NoArgsConstructor
-  public static class CombinePayerInfo implements Serializable{
+  public static class CombinePayerInfo implements Serializable {
     /**
      * 
      * 字段名:用户标识
@@ -404,4 +373,83 @@ public static class SettleInfo implements Serializable{
 
   }
 
+  @Data
+  @NoArgsConstructor
+  public static class H5Info implements Serializable {
+
+    /**
+     * 
+     * 字段名:场景类型
+     * 变量名:type
+     * 是否必填:是
+     * 类型:string(32)
+     * 描述:
+     *  场景类型,枚举值:
+     *  iOS:IOS移动应用;
+     *  Android:安卓移动应用;
+     *  Wap:WAP网站应用;
+     *  示例值:iOS
+     * 
+ */ + @SerializedName(value = "type") + private String type; + + /** + *
+     * 字段名:应用名称
+     * 变量名:app_name
+     * 是否必填:否
+     * 类型:string(64)
+     * 描述:
+     *  应用名称
+     *  示例值:王者荣耀
+     * 
+ */ + @SerializedName(value = "app_name") + private String appName; + + /** + *
+     * 字段名:网站URL
+     * 变量名:app_url
+     * 是否必填:否
+     * 类型:string(128)
+     * 描述:
+     *  网站URL
+     *  示例值:https://pay.qq.com
+     * 
+ */ + @SerializedName(value = "app_url") + private String appUrl; + + /** + *
+     * 字段名:iOS平台BundleID
+     * 变量名:bundle_id
+     * 是否必填:否
+     * 类型:string(128)
+     * 描述:
+     *  iOS平台BundleID
+     *  示例值:com.tencent.wzryiOS
+     * 
+ */ + @SerializedName(value = "bundle_id") + private String bundleId; + + /** + *
+     * 字段名:Android平台PackageName
+     * 变量名:package_name
+     * 是否必填:否
+     * 类型:string(128)
+     * 描述:
+     *  Android平台PackageName
+     *  示例值:com.tencent.tmgp.sgame
+     * 
+ */ + @SerializedName(value = "package_name") + private String packageName; + + } + } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsResult.java new file mode 100644 index 0000000000..cd4edc9d57 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsResult.java @@ -0,0 +1,119 @@ +package com.github.binarywang.wxpay.bean.ecommerce; + +import com.github.binarywang.wxpay.bean.ecommerce.enums.TradeTypeEnum; +import com.github.binarywang.wxpay.v3.util.AesUtils; +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; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; +import java.util.UUID; + +/** + * 合单支付 JSAPI支付结果响应 + */ +@Data +@NoArgsConstructor +public class CombineTransactionsResult implements Serializable { + + /** + *
+   * 字段名:预支付交易会话标识 (APP支付、JSAPI支付 会返回)
+   * 变量名:prepay_id
+   * 是否必填:是
+   * 类型:string(64)
+   * 描述:
+   *  数字和字母。微信生成的预支付会话标识,用于后续接口调用使用。
+   *  示例值:wx201410272009395522657a690389285100
+   * 
+ */ + @SerializedName("prepay_id") + private String prepayId; + + /** + *
+   * 字段名:支付跳转链接   (H5支付 会返回)
+   * 变量名:h5_url
+   * 是否必填:是
+   * 类型:string(512)
+   * 描述:
+   *  支付跳转链接
+   *  示例值:https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=wx2016121516420242444321ca0631331346&package=1405458241
+   * 
+ */ + @SerializedName("h5_url") + private String h5Url; + + /** + *
+   * 字段名:二维码链接  (NATIVE支付 会返回)
+   * 变量名:h5_url
+   * 是否必填:是
+   * 类型:string(512)
+   * 描述:
+   *  二维码链接
+   * 示例值:weixin://pay.weixin.qq.com/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 T getPayInfo(TradeTypeEnum tradeType, String appId, String mchId, PrivateKey privateKey){ + String timestamp = String.valueOf(System.currentTimeMillis() / 1000); + String nonceStr = SignUtils.genRandomStr(); + switch (tradeType){ + case JSAPI: + JsapiResult jsapiResult = new JsapiResult(); + jsapiResult.setAppId(appId).setTimeStamp(timestamp) + .setPackageValue("prepay_id=" + this.prepayId).setNonceStr(nonceStr) + //签名类型,默认为RSA,仅支持RSA。 + .setSignType("RSA").setPaySign(SignUtils.sign(jsapiResult.getSignStr(), privateKey)); + return (T) jsapiResult; + case H5: + return (T) this.h5Url; + case APP: + AppResult appResult = new AppResult(); + appResult.setAppid(appId).setPrepayid(this.prepayId).setPartnerid(mchId) + .setNoncestr(nonceStr).setTimestamp(timestamp) + //暂填写固定值Sign=WXPay + .setPackageValue("Sign=WXPay"); + return (T) appResult; + case NATIVE: + return (T) this.codeUrl; + } + return null; + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/enums/TradeTypeEnum.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/enums/TradeTypeEnum.java new file mode 100644 index 0000000000..c65600ecf1 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/enums/TradeTypeEnum.java @@ -0,0 +1,27 @@ +package com.github.binarywang.wxpay.bean.ecommerce.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 支付方式 + */ +@Getter +@AllArgsConstructor +public enum TradeTypeEnum { + + APP("/v3/combine-transactions/app","/v3/pay/partner/transactions/app"), + JSAPI("/v3/combine-transactions/jsapi","/v3/pay/partner/transactions/jsapi"), + NATIVE("/v3/combine-transactions/native","/v3/pay/partner/transactions/native"), + H5("/v3/combine-transactions/h5","/v3/pay/partner/transactions/h5") + ; + + /** + * 合单url + */ + private String combineUrl; + /** + * 单独下单url + */ + private String partnerUrl; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java index 066fe372cb..c9faaeddfd 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java @@ -1,6 +1,7 @@ package com.github.binarywang.wxpay.service; import com.github.binarywang.wxpay.bean.ecommerce.*; +import com.github.binarywang.wxpay.bean.ecommerce.enums.TradeTypeEnum; import com.github.binarywang.wxpay.exception.WxPayException; /** @@ -63,5 +64,6 @@ public interface EcommerceService { * @param request 请求对象 * @return 预支付交易会话标识, 数字和字母。微信生成的预支付会话标识,用于后续接口调用使用。 */ - CombineTransactionsJsResult combineTransactions(CombineTransactionsJsRequest request) throws WxPayException; + T combineTransactions(TradeTypeEnum tradeType, CombineTransactionsRequest request) throws WxPayException; + } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java index fe916819e7..18f51a45c4 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java @@ -1,6 +1,7 @@ package com.github.binarywang.wxpay.service.impl; import com.github.binarywang.wxpay.bean.ecommerce.*; +import com.github.binarywang.wxpay.bean.ecommerce.enums.TradeTypeEnum; import com.github.binarywang.wxpay.exception.WxPayException; import com.github.binarywang.wxpay.service.EcommerceService; import com.github.binarywang.wxpay.service.WxPayService; @@ -8,6 +9,7 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; import java.net.URI; @@ -40,10 +42,12 @@ public ApplymentsStatusResult queryApplyStatusByOutRequestNo(String outRequestNo } @Override - public CombineTransactionsJsResult combineTransactions(CombineTransactionsJsRequest request) throws WxPayException { - String url = String.format("%s/v3/combine-transactions/jsapi", this.payService.getPayBaseUrl()); + public T combineTransactions(TradeTypeEnum tradeType, CombineTransactionsRequest request) throws WxPayException { + String url = this.payService.getPayBaseUrl() + tradeType.getCombineUrl(); String response = this.payService.postV3(url, GSON.toJson(request)); - return GSON.fromJson(response, CombineTransactionsJsResult.class); + CombineTransactionsResult result = GSON.fromJson(response, CombineTransactionsResult.class); + return result.getPayInfo(tradeType, request.getCombineAppid(), + request.getCombineMchid(), payService.getConfig().getPrivateKey()); } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/util/SignUtils.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/util/SignUtils.java new file mode 100644 index 0000000000..275a8d51ba --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/util/SignUtils.java @@ -0,0 +1,47 @@ +package com.github.binarywang.wxpay.v3.util; + +import java.security.*; +import java.util.Base64; +import java.util.Random; + +public class SignUtils { + + public static String sign(String string, PrivateKey privateKey){ + try { + Signature sign = Signature.getInstance("SHA256withRSA"); + sign.initSign(privateKey); + sign.update(string.getBytes()); + + return Base64.getEncoder().encodeToString(sign.sign()); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("当前Java环境不支持SHA256withRSA", e); + } catch (SignatureException e) { + throw new RuntimeException("签名计算失败", e); + } catch (InvalidKeyException e) { + throw new RuntimeException("无效的私钥", e); + } + } + + /** + * 随机生成32位字符串. + */ + public static String genRandomStr(){ + return genRandomStr(32); + } + + /** + * 生成随机字符串 + * @param length 字符串长度 + * @return + */ + public static String genRandomStr(int length) { + String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + Random random = new Random(); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < length; i++) { + int number = random.nextInt(base.length()); + sb.append(base.charAt(number)); + } + return sb.toString(); + } +} From 97409a7fbc6868d7daae26d4a1f84a3bb298b398 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9B=BE=E6=B5=A9?= Date: Tue, 8 Sep 2020 15:02:46 +0800 Subject: [PATCH 6/8] =?UTF-8?q?new:=E7=94=B5=E5=95=86=E6=94=B6=E4=BB=98?= =?UTF-8?q?=E9=80=9A=E6=99=AE=E9=80=9A=E6=94=AF=E4=BB=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ecommerce/CombineTransactionsRequest.java | 5 +- .../ecommerce/PartnerTransactionsRequest.java | 654 ++++++++++++++++++ ...onsResult.java => TransactionsResult.java} | 7 +- .../bean/ecommerce/enums/TradeTypeEnum.java | 4 +- .../wxpay/service/EcommerceService.java | 18 +- .../service/impl/EcommerceServiceImpl.java | 12 +- 6 files changed, 685 insertions(+), 15 deletions(-) create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/PartnerTransactionsRequest.java rename weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/{CombineTransactionsResult.java => TransactionsResult.java} (94%) diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsRequest.java index 3b138b4882..61fac62ea9 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsRequest.java @@ -8,7 +8,10 @@ import java.util.List; /** - * 合单支付 对象 + * 合单支付API + *
+ * 文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pages/e-combine.shtml
+ * 
*/ @Data @NoArgsConstructor diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/PartnerTransactionsRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/PartnerTransactionsRequest.java new file mode 100644 index 0000000000..a324b616bd --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/PartnerTransactionsRequest.java @@ -0,0 +1,654 @@ +package com.github.binarywang.wxpay.bean.ecommerce; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.List; + +/** + * 普通支付(电商收付通)API + *
+ * 文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pages/e_transactions.shtml
+ * 
+ */ +@Data +@NoArgsConstructor +public class PartnerTransactionsRequest implements Serializable { + + /** + *
+   * 字段名:服务商公众号ID
+   * 变量名:sp_appid
+   * 是否必填:是
+   * 类型:string(32)
+   * 描述:
+   *  服务商申请的公众号或移动应用appid
+   *  示例值:wx8888888888888888
+   * 
+ */ + @SerializedName(value = "sp_appid") + private String spAppid; + + /** + *
+   * 字段名:服务商户号
+   * 变量名:sp_mchid
+   * 是否必填:是
+   * 类型:string(32)
+   * 描述:
+   *  服务商户号,由微信支付生成并下发
+   *  示例值:1230000109
+   * 
+ */ + @SerializedName(value = "sp_mchid") + private String spMchid; + + /** + *
+   * 字段名:子商户公众号ID
+   * 变量名:sub_appid
+   * 是否必填:否
+   * 类型:string(32)
+   * 描述:
+   *  子商户申请的公众号或移动应用appid。
+   *  示例值:wxd678efh567hg6999
+   * 
+ */ + @SerializedName(value = "sub_appid") + private String subAppid; + /** + *
+   * 字段名:二级商户号
+   * 变量名:sub_mchid
+   * 是否必填:是
+   * 类型:string(32)
+   * 描述:
+   *  二级商户的商户号,有微信支付生成并下发。
+   *  示例值:1900000109
+   * 
+ */ + @SerializedName(value = "sub_mchid") + private String subMchid; + + /** + *
+   * 字段名:商品描述
+   * 变量名:description
+   * 是否必填:是
+   * 类型:string(127)
+   * 描述:
+   *  商品描述
+   *  示例值:Image形象店-深圳腾大-QQ公仔
+   * 
+ */ + @SerializedName(value = "description") + private String description; + + /** + *
+   * 字段名:商户订单号
+   * 变量名:out_trade_no
+   * 是否必填:是
+   * 类型:string(127)
+   * 描述:
+   *  商户系统内部订单号, 只能是数字、大小写字母_-*且在同一个商户号下唯一,详见【商户订单号】
+   *  特殊规则:最小字符长度为6
+   *  示例值:1217752501201407033233368018
+   * 
+ */ + @SerializedName(value = "out_trade_no") + private String out_trade_no; + + /** + *
+   * 字段名:交易结束时间
+   * 变量名:time_expire
+   * 是否必填:否
+   * 类型:string(14)
+   * 描述:
+   *  订单失效时间,遵循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秒。
+   *  示例值:2019-12-31T15:59:60+08:00
+   * 
+ */ + @SerializedName(value = "time_expire") + private String timeExpire; + + /** + *
+   * 字段名:附加数据
+   * 变量名:attach
+   * 是否必填:否
+   * 类型:string(128)
+   * 描述:
+   *  附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用。
+   *  示例值:自定义数据
+   * 
+ */ + @SerializedName(value = "attach") + private String attach; + + /** + *
+   * 字段名:通知地址
+   * 变量名:notify_url
+   * 是否必填:是
+   * 类型:string(127)
+   * 描述:
+   *  通知URL必须为直接可访问的URL,不允许携带查询串。
+   *  示例值:https://www.weixin.qq.com/wxpay/pay.php
+   * 
+ */ + @SerializedName(value = "notify_url") + private String notifyUrl; + + /** + *
+   * 字段名:订单优惠标记
+   * 变量名:goods_tag
+   * 是否必填:否
+   * 类型:string(32)
+   * 描述:
+   *  订单优惠标记
+   *  示例值:WXG
+   * 
+ */ + @SerializedName(value = "goods_tag") + private String goodsTag; + + /** + *
+   * 字段名:+结算信息
+   * 变量名:settle_info
+   * 是否必填:否
+   * 类型:Object
+   * 描述:结算信息
+   * 
+ */ + @SerializedName(value = "settle_info") + private SettleInfo settleInfo; + + /** + *
+   * 字段名:订单金额
+   * 变量名:amount
+   * 是否必填:是
+   * 类型:object
+   * 描述:
+   *  订单金额信息
+   * 
+ */ + @SerializedName(value = "amount") + private Amount amount; + + /** + *
+   * 字段名:优惠功能
+   * 变量名:detail
+   * 是否必填:否
+   * 类型:object
+   * 描述:
+   *  优惠功能
+   * 
+ */ + @SerializedName(value = "detail") + private Discount detail; + + /** + *
+   * 字段名:支付者
+   * 变量名:payer
+   * 是否必填:是(仅JSAPI支付必传)
+   * 类型:object
+   * 描述:
+   *  支付者信息
+   * 
+ */ + @SerializedName(value = "payer") + private Payer payer; + + /** + *
+   * 字段名:场景信息
+   * 变量名:scene_info
+   * 是否必填:是(仅H5支付必传)
+   * 类型:object
+   * 描述:
+   *  支付场景描述
+   * 
+ */ + @SerializedName(value = "scene_info") + private SceneInfo sceneInfo; + + @Data + @NoArgsConstructor + public static class Discount implements Serializable { + + /** + *
+     * 字段名:订单原价
+     * 变量名:cost_price
+     * 是否必填:否
+     * 类型:int64
+     * 描述:
+     *  1、商户侧一张小票订单可能被分多次支付,订单原价用于记录整张小票的交易金额。
+     *  2、当订单原价与支付金额不相等,则不享受优惠。
+     *  3、该字段主要用于防止同一张小票分多次支付,以享受多次优惠的情况,正常支付订单不必上传此参数。
+     *  示例值:608800
+     * 
+ */ + @SerializedName(value = "cost_price") + private Integer costPrice; + + /** + *
+     * 字段名:商品小票ID
+     * 变量名:invoice_id
+     * 是否必填:否
+     * 类型:string(32)
+     * 描述:
+     *  商品小票ID
+     *  示例值:微信123
+     * 
+ */ + @SerializedName(value = "invoice_id") + private Integer invoiceId; + + /** + *
+     * 字段名:单品列表
+     * 变量名:goods_detail
+     * 是否必填:否
+     * 类型:array
+     * 描述:
+     *  单品列表信息
+     *  条目个数限制:【1,undefined】
+     * 
+ */ + @SerializedName(value = "goods_detail") + private List goodsDetails; + } + + @Data + @NoArgsConstructor + public static class Amount implements Serializable { + + /** + *
+     * 字段名:总金额
+     * 变量名:total
+     * 是否必填:是
+     * 类型:int64
+     * 描述:
+     *  订单总金额,单位为分。
+     *  示例值:100
+     * 
+ */ + @SerializedName(value = "total") + private Integer total; + + /** + *
+     * 字段名:币类型
+     * 变量名:currency
+     * 是否必填:否
+     * 类型:string(16)
+     * 描述:
+     *  CNY:人民币,境内商户号仅支持人民币。
+     *  示例值:CNY
+     * 
+ */ + @SerializedName(value = "currency") + private String currency; + + } + + @Data + @NoArgsConstructor + public static class Payer implements Serializable { + + /** + *
+     * 字段名:用户服务标识
+     * 变量名:sp_openid
+     * 是否必填:是
+     * 类型:string(128)
+     * 描述:
+     *  用户在服务商appid下的唯一标识。
+     *  示例值:oUpF8uMuAJO_M2pxb1Q9zNjWeS6o
+     * 
+ */ + @SerializedName(value = "sp_openid") + private String spOpenid; + + /** + *
+     * 字段名:用户子标识
+     * 变量名:sub_openid
+     * 是否必填:否
+     * 类型:string(128)
+     * 描述:
+     *  用户在子商户appid下的唯一标识。
+     *  示例值:oUpF8uMuAJO_M2pxb1Q9zNjWeS6o
+     * 
+ */ + @SerializedName(value = "sub_openid") + private String subOpenid; + + } + + @Data + @NoArgsConstructor + public static class SettleInfo implements Serializable{ + /** + *
+     * 字段名:是否指定分账
+     * 变量名:profit_sharing
+     * 是否必填:否
+     * 类型:bool
+     * 描述:
+     *  是否分账,与外层profit_sharing同时存在时,以本字段为准。
+     *  true:是
+     *  false:否
+     *  示例值:true
+     * 
+ */ + @SerializedName(value = "profit_sharing") + private Boolean profitSharing; + + /** + *
+     * 字段名:补差金额
+     * 变量名:subsidy_amount
+     * 是否必填:否
+     * 类型:int64
+     * 描述:
+     *  SettleInfo.profit_sharing为true时,该金额才生效。
+     *    注意:单笔订单最高补差金额为5000元
+     *  示例值:10
+     * 
+ */ + @SerializedName(value = "subsidy_amount") + private BigDecimal subsidyAmount; + + } + + @Data + @NoArgsConstructor + public static class GoodsDetail implements Serializable { + + /** + *
+     * 字段名:商户侧商品编码
+     * 变量名:merchant_goods_id
+     * 是否必填:是
+     * 类型:string(32)
+     * 描述:
+     *  由半角的大小写字母、数字、中划线、下划线中的一种或几种组成。
+     * 示例值:商品编码
+     * 
+ */ + @SerializedName(value = "merchant_goods_id") + private String merchantGoodsId; + + /** + *
+     * 字段名:微信侧商品编码
+     * 变量名:wechatpay_goods_id
+     * 是否必填:否
+     * 类型:string(32)
+     * 描述:
+     *  微信支付定义的统一商品编号(没有可不传)
+     * 示例值:1001
+     * 
+ */ + @SerializedName(value = "wechatpay_goods_id") + private String wechatpayGoodsId; + + /** + *
+     * 字段名:商品名称
+     * 变量名:goods_name
+     * 是否必填:否
+     * 类型:string(256)
+     * 描述:
+     *  商品的实际名称
+     * 示例值:iPhoneX 256G
+     * 
+ */ + @SerializedName(value = "goods_name") + private String goodsName; + + /** + *
+     * 字段名:商品数量
+     * 变量名:quantity
+     * 是否必填:是
+     * 类型:int64
+     * 描述:
+     *  用户购买的数量
+     * 示例值:1
+     * 
+ */ + @SerializedName(value = "quantity") + private Integer quantity; + + /** + *
+     * 字段名:商品单价
+     * 变量名:unit_price
+     * 是否必填:是
+     * 类型:int64
+     * 描述:
+     *  商品单价,单位为分
+     * 示例值:828800
+     * 
+ */ + @SerializedName(value = "unit_price") + private Integer unitPrice; + } + + @Data + @NoArgsConstructor + public static class SceneInfo implements Serializable { + /** + *
+     * 字段名:商户端设备号
+     * 变量名:device_id
+     * 是否必填:否
+     * 类型:string(16)
+     * 描述:
+     *  终端设备号(门店号或收银设备ID)。
+     *  特殊规则:长度最小7个字节
+     *  示例值:POS1:1
+     * 
+ */ + @SerializedName(value = "device_id") + private String deviceId; + + /** + *
+     * 字段名:用户终端IP
+     * 变量名:payer_client_ip
+     * 是否必填:是
+     * 类型:string(45)
+     * 描述:
+     *  用户端实际ip
+     *  格式: ip(ipv4+ipv6)
+     *  示例值:14.17.22.32
+     * 
+ */ + @SerializedName(value = "payer_client_ip") + private String payerClientIp; + + /** + *
+     * 字段名:H5场景信息
+     * 变量名:h5_info
+     * 是否必填:否(H5支付必填)
+     * 类型:object
+     * 描述:
+     *  H5场景信息
+     * 
+ */ + @SerializedName(value = "h5_info") + private H5Info h5Info; + + /** + *
+     * 字段名:商户门店信息
+     * 变量名:store_info
+     * 是否必填:否(H5支付必填)
+     * 类型:object
+     * 描述:
+     *  商户门店信息
+     * 
+ */ + @SerializedName(value = "store_info") + private StoreInfo storeInfo; + + } + + @Data + @NoArgsConstructor + public static class H5Info implements Serializable { + + /** + *
+     * 字段名:场景类型
+     * 变量名:type
+     * 是否必填:是
+     * 类型:string(32)
+     * 描述:
+     *  场景类型,枚举值:
+     *  iOS:IOS移动应用;
+     *  Android:安卓移动应用;
+     *  Wap:WAP网站应用;
+     *  示例值:iOS
+     * 
+ */ + @SerializedName(value = "type") + private String type; + + /** + *
+     * 字段名:应用名称
+     * 变量名:app_name
+     * 是否必填:否
+     * 类型:string(64)
+     * 描述:
+     *  应用名称
+     *  示例值:王者荣耀
+     * 
+ */ + @SerializedName(value = "app_name") + private String appName; + + /** + *
+     * 字段名:网站URL
+     * 变量名:app_url
+     * 是否必填:否
+     * 类型:string(128)
+     * 描述:
+     *  网站URL
+     *  示例值:https://pay.qq.com
+     * 
+ */ + @SerializedName(value = "app_url") + private String appUrl; + + /** + *
+     * 字段名:iOS平台BundleID
+     * 变量名:bundle_id
+     * 是否必填:否
+     * 类型:string(128)
+     * 描述:
+     *  iOS平台BundleID
+     *  示例值:com.tencent.wzryiOS
+     * 
+ */ + @SerializedName(value = "bundle_id") + private String bundleId; + + /** + *
+     * 字段名:Android平台PackageName
+     * 变量名:package_name
+     * 是否必填:否
+     * 类型:string(128)
+     * 描述:
+     *  Android平台PackageName
+     *  示例值:com.tencent.tmgp.sgame
+     * 
+ */ + @SerializedName(value = "package_name") + private String packageName; + + } + + @Data + @NoArgsConstructor + public static class StoreInfo implements Serializable { + + /** + *
+     * 字段名:门店编号
+     * 变量名:id
+     * 是否必填:否
+     * 类型:string(32)
+     * 描述:
+     *  商户侧门店编号
+     * 示例值:0001
+     * 
+ */ + @SerializedName(value = "id") + private String id; + + /** + *
+     * 字段名:门店名称
+     * 变量名:name
+     * 是否必填:是
+     * 类型:string(256)
+     * 描述:
+     *  商户侧门店名称
+     * 示例值:腾讯大厦分店
+     * 
+ */ + @SerializedName(value = "name") + private String name; + + /** + *
+     * 字段名:地区编码
+     * 变量名:area_code
+     * 是否必填:是
+     * 类型:string(32)
+     * 描述:
+     *  地区编码,详细请见省市区编号对照表(https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/ecommerce/applyments/chapter4_1.shtml)。
+     * 示例值:440305
+     * 
+ */ + @SerializedName(value = "area_code") + private String areaCode; + + /** + *
+     * 字段名:详细地址
+     * 变量名:address
+     * 是否必填:是
+     * 类型:string(512)
+     * 描述:
+     *  详细的商户门店地址
+     * 示例值:广东省深圳市南山区科技中一道10000号
+     * 
+ */ + @SerializedName(value = "address") + private String address; + + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/TransactionsResult.java similarity index 94% rename from weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsResult.java rename to weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/TransactionsResult.java index cd4edc9d57..3ce288a701 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsResult.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/TransactionsResult.java @@ -1,7 +1,6 @@ package com.github.binarywang.wxpay.bean.ecommerce; import com.github.binarywang.wxpay.bean.ecommerce.enums.TradeTypeEnum; -import com.github.binarywang.wxpay.v3.util.AesUtils; import com.github.binarywang.wxpay.v3.util.SignUtils; import com.google.gson.annotations.SerializedName; import lombok.Data; @@ -10,17 +9,13 @@ import java.io.Serializable; import java.security.PrivateKey; -import java.util.HashMap; -import java.util.Map; -import java.util.Random; -import java.util.UUID; /** * 合单支付 JSAPI支付结果响应 */ @Data @NoArgsConstructor -public class CombineTransactionsResult implements Serializable { +public class TransactionsResult implements Serializable { /** *
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/enums/TradeTypeEnum.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/enums/TradeTypeEnum.java
index c65600ecf1..03dd2cb36b 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/enums/TradeTypeEnum.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/enums/TradeTypeEnum.java
@@ -19,9 +19,9 @@ public enum TradeTypeEnum {
   /**
    * 合单url
    */
-  private String combineUrl;
+  private final String combineUrl;
   /**
    * 单独下单url
    */
-  private String partnerUrl;
+  private final String partnerUrl;
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java
index c9faaeddfd..add7c17d1d 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java
@@ -56,14 +56,26 @@ public interface EcommerceService {
 
   /**
    * 
-   * 合单下单-JS支付API.
+   * 合单支付API(APP支付、JSAPI支付、H5支付、NATIVE支付).
    * 请求URL:https://api.mch.weixin.qq.com/v3/combine-transactions/jsapi
-   * 文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/combine/chapter3_2.shtml
+   * 文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pages/e-combine.shtml
    * 
* + * @param tradeType 支付方式 * @param request 请求对象 - * @return 预支付交易会话标识, 数字和字母。微信生成的预支付会话标识,用于后续接口调用使用。 + * @return 调起支付需要的参数 */ T combineTransactions(TradeTypeEnum tradeType, CombineTransactionsRequest request) throws WxPayException; + /** + *
+   *  服务商模式普通支付API(APP支付、JSAPI支付、H5支付、NATIVE支付).
+   *  请求URL:https://api.mch.weixin.qq.com/v3/pay/partner/transactions/jsapi
+   *  文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pages/transactions_sl.shtml
+   *  
+ * @param tradeType 支付方式 + * @param request 请求对象 + * @return 调起支付需要的参数 + */ + T partnerTransactions(TradeTypeEnum tradeType, PartnerTransactionsRequest request) throws WxPayException; } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java index 18f51a45c4..45205ac06a 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java @@ -9,7 +9,6 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import lombok.RequiredArgsConstructor; -import org.apache.commons.lang3.StringUtils; import java.net.URI; @@ -45,10 +44,17 @@ public ApplymentsStatusResult queryApplyStatusByOutRequestNo(String outRequestNo public T combineTransactions(TradeTypeEnum tradeType, CombineTransactionsRequest request) throws WxPayException { String url = this.payService.getPayBaseUrl() + tradeType.getCombineUrl(); String response = this.payService.postV3(url, GSON.toJson(request)); - CombineTransactionsResult result = GSON.fromJson(response, CombineTransactionsResult.class); + TransactionsResult result = GSON.fromJson(response, TransactionsResult.class); return result.getPayInfo(tradeType, request.getCombineAppid(), request.getCombineMchid(), payService.getConfig().getPrivateKey()); } - + @Override + public T partnerTransactions(TradeTypeEnum tradeType, PartnerTransactionsRequest request) throws WxPayException { + String url = this.payService.getPayBaseUrl() + tradeType.getPartnerUrl(); + String response = this.payService.postV3(url, GSON.toJson(request)); + TransactionsResult result = GSON.fromJson(response, TransactionsResult.class); + return result.getPayInfo(tradeType, request.getSpAppid(), + request.getSpMchid(), payService.getConfig().getPrivateKey()); + } } From f18c62b1525ef09175809498bf130344aede2eab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9B=BE=E6=B5=A9?= Date: Thu, 10 Sep 2020 11:00:33 +0800 Subject: [PATCH 7/8] =?UTF-8?q?new:=E7=94=B5=E5=95=86=E6=94=B6=E4=BB=98?= =?UTF-8?q?=E9=80=9A=E6=94=AF=E4=BB=98=E5=9B=9E=E8=B0=83=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CombineTransactionsNotifyResult.java | 359 +++++++++++ .../wxpay/bean/ecommerce/NotifyResponse.java | 51 ++ .../PartnerTransactionsNotifyResult.java | 592 ++++++++++++++++++ .../wxpay/bean/ecommerce/SignatureHeader.java | 35 ++ .../bean/ecommerce/TransactionsResult.java | 2 +- .../bean/ecommerce/enums/TradeTypeEnum.java | 2 +- .../wxpay/service/EcommerceService.java | 24 + .../service/impl/EcommerceServiceImpl.java | 57 ++ 8 files changed, 1120 insertions(+), 2 deletions(-) create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsNotifyResult.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/NotifyResponse.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/PartnerTransactionsNotifyResult.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SignatureHeader.java diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsNotifyResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsNotifyResult.java new file mode 100644 index 0000000000..889b835af6 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/CombineTransactionsNotifyResult.java @@ -0,0 +1,359 @@ +package com.github.binarywang.wxpay.bean.ecommerce; + +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/wxpay/pay/combine/chapter3_7.shtml
+ * 
+ */ +@Data +@NoArgsConstructor +public class CombineTransactionsNotifyResult implements Serializable { + + /** + * 源数据 + */ + private NotifyResponse rawData; + + /** + *
+     * 字段名:合单商户appid
+     * 变量名:combine_appid
+     * 是否必填:是
+     * 类型:string(32)
+     * 描述:
+     *  合单发起方的appid。(即电商平台appid)
+     *  示例值:wxd678efh567hg6787
+     * 
+ */ + @SerializedName(value = "combine_appid") + private String combineAppid; + + /** + *
+     * 字段名:合单商户号
+     * 变量名:combine_mchid
+     * 是否必填:是
+     * 类型:string(32)
+     * 描述:
+     *  合单发起方商户号。(即电商平台mchid)
+     *  示例值:1900000109
+     * 
+ */ + @SerializedName(value = "combine_mchid") + private String combineMchid; + + /** + *
+     * 字段名:合单商户订单号
+     * 变量名:combine_out_trade_no
+     * 是否必填:是
+     * 类型:string(32)
+     * 描述:
+     *  合单支付总订单号,要求32个字符内,只能是数字、大小写字母_-|*@ ,且在同一个商户号下唯一。
+     *  示例值:P20150806125346
+     * 
+ */ + @SerializedName(value = "combine_out_trade_no") + private String combineOutTradeNo; + + /** + *
+     * 字段名:+场景信息
+     * 变量名:scene_info
+     * 是否必填:否
+     * 类型:object
+     * 描述:支付场景信息描述
+     * 
+ */ + @SerializedName(value = "scene_info") + private SceneInfo sceneInfo; + + /** + *
+     * 字段名:+子单信息
+     * 变量名:sub_orders
+     * 是否必填:是
+     * 类型:array
+     * 描述:
+     *  最多支持子单条数:50
+     *
+     * 
+ */ + @SerializedName(value = "sub_orders") + private List subOrders; + + /** + *
+     * 字段名:+支付者
+     * 变量名:combine_payer_info
+     * 是否必填:否
+     * 类型:object
+     * 描述:示例值:见请求示例
+     * 
+ */ + @SerializedName(value = "combine_payer_info") + private CombinePayerInfo combinePayerInfo; + + @Data + @NoArgsConstructor + public static class SubOrders implements Serializable { + /** + *
+     * 字段名:子单商户号
+     * 变量名:mchid
+     * 是否必填:是
+     * 类型:string(32)
+     * 描述:
+     *  子单发起方商户号,必须与发起方Appid有绑定关系。(即电商平台mchid)
+     *  示例值:1900000109
+     * 
+ */ + @SerializedName(value = "mchid") + private String mchid; + + /** + *
+     * 字段名:交易类型
+     * 变量名:trade_type
+     * 是否必填:是
+     * 类型:string (16)
+     * 描述:
+     *  枚举值:
+     *  NATIVE:扫码支付
+     *  JSAPI:公众号支付
+     *  APP:APP支付
+     *  MWEB:H5支付
+     *  示例值: JSAPI
+     * 
+ */ + @SerializedName(value = "trade_type") + private String tradeType; + + /** + *
+     * 字段名:交易状态
+     * 变量名:trade_state
+     * 是否必填:是
+     * 类型:string (32)
+     * 描述:
+     *  枚举值:
+     *  SUCCESS:支付成功
+     *  REFUND:转入退款
+     *  NOTPAY:未支付
+     *  CLOSED:已关闭
+     *  USERPAYING:用户支付中
+     *  PAYERROR:支付失败(其他原因,如银行返回失败)
+     *  示例值: SUCCESS
+     * 
+ */ + @SerializedName(value = "trade_state") + private String tradeState; + + /** + *
+     * 字段名:付款银行
+     * 变量名:bank_type
+     * 是否必填:否
+     * 类型:string(16)
+     * 描述:
+     *  银行类型,采用字符串类型的银行标识。
+     *  示例值:CMC
+     * 
+ */ + @SerializedName(value = "bank_type") + private String bankType; + + /** + *
+     * 字段名:附加信息
+     * 变量名:attach
+     * 是否必填:是
+     * 类型:string(128)
+     * 描述:
+     *  附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用。
+     *  示例值:深圳分店
+     * 
+ */ + @SerializedName(value = "attach") + private String attach; + + /** + *
+     * 字段名:支付完成时间
+     * 变量名:success_time
+     * 是否必填:是
+     * 类型:string(16)
+     * 描述:
+     *  订单支付时间,遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss:sss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss:sss表示时分秒毫秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35.120+08:00表示,北京时间2015年5月20日 13点29分35秒。
+     *  示例值:2015-05-20T13:29:35.120+08:00
+     * 
+ */ + @SerializedName(value = "success_time") + private String successTime; + + /** + *
+     * 字段名:微信订单号
+     * 变量名:transaction_id
+     * 是否必填:是
+     * 类型:string(32)
+     * 描述:
+     *  微信支付订单号。
+     *  示例值: 1009660380201506130728806387
+     * 
+ */ + @SerializedName(value = "transaction_id") + private String transactionId; + + /** + *
+     * 字段名:子单商户订单号
+     * 变量名:out_trade_no
+     * 是否必填:是
+     * 类型:string(32)
+     * 描述:
+     *  商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*@ ,且在同一个商户号下唯一。
+     *  特殊规则:最小字符长度为6
+     *  示例值:20150806125346
+     * 
+ */ + @SerializedName(value = "out_trade_no") + private String outTradeNo; + + /** + *
+     * 字段名:二级商户号
+     * 变量名:sub_mchid
+     * 是否必填:是
+     * 类型:string(32)
+     * 描述:
+     *  二级商户商户号,由微信支付生成并下发。
+     *  注意:仅适用于电商平台 服务商
+     *  示例值:1900000109
+     * 
+ */ + @SerializedName(value = "sub_mchid") + private String subMchid; + + /** + *
+     * 字段名:+订单金额
+     * 变量名:amount
+     * 是否必填:是
+     * 类型:object
+     * 描述:订单金额信息
+     * 
+ */ + @SerializedName(value = "amount") + private Amount amount; + + } + + @Data + @NoArgsConstructor + public static class SceneInfo implements Serializable { + /** + *
+     * 字段名:商户端设备号
+     * 变量名:device_id
+     * 是否必填:否
+     * 类型:string(16)
+     * 描述:
+     *  终端设备号(门店号或收银设备ID)。
+     *  特殊规则:长度最小7个字节
+     *  示例值:POS1:1
+     * 
+ */ + @SerializedName(value = "device_id") + private String deviceId; + + } + + @Data + @NoArgsConstructor + public static class CombinePayerInfo implements Serializable { + /** + *
+     * 字段名:用户标识
+     * 变量名:openid
+     * 是否必填:是
+     * 类型:string(128)
+     * 描述:
+     *  使用合单appid获取的对应用户openid。是用户在商户appid下的唯一标识。
+     *  示例值:oUpF8uMuAJO_M2pxb1Q9zNjWeS6o
+     * 
+ */ + @SerializedName(value = "openid") + private String openid; + + } + + @Data + @NoArgsConstructor + public static class Amount implements Serializable { + /** + *
+     * 字段名:标价金额
+     * 变量名:total_amount
+     * 是否必填:是
+     * 类型:int64
+     * 描述:
+     *  子单金额,单位为分。
+     *  示例值:100
+     * 
+ */ + @SerializedName(value = "total_amount") + private Integer totalAmount; + + /** + *
+     * 字段名:标价币种
+     * 变量名:currency
+     * 是否必填:是
+     * 类型:string(8)
+     * 描述:
+     *  符合ISO 4217标准的三位字母代码,人民币:CNY。
+     *  示例值:CNY
+     * 
+ */ + @SerializedName(value = "currency") + private String currency; + + /** + *
+     * 字段名:现金支付金额
+     * 变量名:payer_amount
+     * 是否必填:是
+     * 类型:int64
+     * 描述:
+     *  订单现金支付金额。
+     *  示例值:10
+     * 
+ */ + @SerializedName(value = "payer_amount") + private Integer payerAmount; + + /** + *
+     * 字段名:现金支付币种
+     * 变量名:payer_currency
+     * 是否必填:是
+     * 类型:string(8)
+     * 描述:
+     *  货币类型,符合ISO 4217标准的三位字母代码,默认人民币:CNY。
+     *  示例值: CNY
+     * 
+ */ + @SerializedName(value = "payer_currency") + private String payerCurrency; + } + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/NotifyResponse.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/NotifyResponse.java new file mode 100644 index 0000000000..c4f0a387ee --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/NotifyResponse.java @@ -0,0 +1,51 @@ +package com.github.binarywang.wxpay.bean.ecommerce; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 通知数据 + */ +@Data +@NoArgsConstructor +public class NotifyResponse implements Serializable { + + @SerializedName(value = "id") + private String id; + + @SerializedName(value = "create_time") + private String createTime; + + @SerializedName(value = "event_type") + private String eventType; + + @SerializedName(value = "resource_type") + private String resourceType; + + @SerializedName(value = "resource") + private Resource resource; + + @SerializedName(value = "summary") + private String summary; + + @Data + @NoArgsConstructor + public static class Resource implements Serializable { + + @SerializedName(value = "algorithm") + private String algorithm; + + @SerializedName(value = "ciphertext") + private String ciphertext; + + @SerializedName(value = "associated_data") + private String associatedData; + + @SerializedName(value = "nonce") + private String nonce; + } + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/PartnerTransactionsNotifyResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/PartnerTransactionsNotifyResult.java new file mode 100644 index 0000000000..77f3aa3bc2 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/PartnerTransactionsNotifyResult.java @@ -0,0 +1,592 @@ +package com.github.binarywang.wxpay.bean.ecommerce; + +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/wxpay/ecommerce/e_transactions/chapter3_11.shtml
+ * 
+ */ +@Data +@NoArgsConstructor +public class PartnerTransactionsNotifyResult implements Serializable { + + /** + * 源数据 + */ + private NotifyResponse rawData; + + /** + *
+   * 字段名:服务商公众号ID
+   * 变量名:sp_appid
+   * 是否必填:是
+   * 类型:string(32)
+   * 描述:
+   *  服务商申请的公众号或移动应用appid。
+   *  示例值:wx8888888888888888
+   * 
+ */ + @SerializedName(value = "sp_appid") + private String spAppid; + + /** + *
+   * 字段名:服务商户号
+   * 变量名:sp_mchid
+   * 是否必填:是
+   * 类型:string(32)
+   * 描述:
+   *  服务商户号,由微信支付生成并下发
+   *  示例值:1230000109
+   * 
+ */ + @SerializedName(value = "sp_mchid") + private String spMchid; + + /** + *
+   * 字段名:二级商户公众号ID
+   * 变量名:sub_appid
+   * 是否必填:否
+   * 类型:string(32)
+   * 描述:
+   *  二级商户申请的公众号或移动应用appid。
+   *  示例值:wxd678efh567hg6999
+   * 
+ */ + @SerializedName(value = "sub_appid") + private String subAppid; + + /** + *
+   * 字段名:二级商户号
+   * 变量名:sub_mchid
+   * 是否必填:是
+   * 类型:string(32)
+   * 描述:
+   *  二级商户的商户号,有微信支付生成并下发。
+   *  示例值:1900000109
+   * 
+ */ + @SerializedName(value = "sub_mchid") + private String subMchid; + + /** + *
+   * 字段名:+商户订单号
+   * 变量名:out_trade_no
+   * 是否必填:是
+   * 类型:string(32)
+   * 描述:商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一,详见【商户订单号】。
+   * 特殊规则:最小字符长度为6
+   * 示例值:1217752501201407033233368018
+   * 
+ */ + @SerializedName(value = "out_trade_no") + private String outTradeNo; + + /** + *
+   * 字段名:微信支付订单号
+   * 变量名:transaction_id
+   * 是否必填:否
+   * 类型:string(32)
+   * 描述:微信支付系统生成的订单号。
+   * 示例值:1217752501201407033233368018
+   * 
+ */ + @SerializedName(value = "transaction_id") + private String transactionId; + + /** + *
+   * 字段名:交易类型
+   * 变量名:trade_type
+   * 是否必填:否
+   * 类型:string(16)
+   * 描述:交易类型,枚举值:
+   *  JSAPI:公众号支付
+   *  NATIVE:扫码支付
+   *  APP:APP支付
+   *  MICROPAY:付款码支付
+   *  MWEB:H5支付
+   *  FACEPAY:刷脸支付
+   *
+   * 示例值: MICROPAY
+   * 
+ */ + @SerializedName(value = "trade_type") + private String tradeType; + + /** + *
+   * 字段名:交易状态
+   * 变量名:trade_state
+   * 是否必填:是
+   * 类型:string(32)
+   * 描述:交易状态,枚举值:
+   *  SUCCESS:支付成功
+   *  REFUND:转入退款
+   *  NOTPAY:未支付
+   *  CLOSED:已关闭
+   *  REVOKED:已撤销(付款码支付)
+   *  USERPAYING:用户支付中(付款码支付)
+   *  PAYERROR:支付失败(其他原因,如银行返回失败)
+   *
+   * 示例值:SUCCESS
+   * 
+ */ + @SerializedName(value = "trade_state") + private String tradeState; + + /** + *
+   * 字段名:交易状态描述
+   * 变量名:trade_state_desc
+   * 是否必填:是
+   * 类型:string(256)
+   * 描述:交易状态描述
+   * 示例值:支付失败,请重新下单支付
+   * 
+ */ + @SerializedName(value = "trade_state_desc") + private String tradeStateDesc; + + /** + *
+   * 字段名:付款银行
+   * 变量名:bank_type
+   * 是否必填:否
+   * 类型:string(16)
+   * 描述:银行类型,采用字符串类型的银行标识。
+   * 示例值:CMC
+   * 
+ */ + @SerializedName(value = "bank_type") + private String bankType; + + /** + *
+   * 字段名:附加数据
+   * 变量名:attach
+   * 是否必填:否
+   * 类型:string(128)
+   * 描述:附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用
+   * 示例值:自定义数据
+   * 
+ */ + @SerializedName(value = "attach") + private String attach; + + /** + *
+   * 字段名:支付完成时间
+   * 变量名:success_time
+   * 是否必填:否
+   * 类型:string(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; + + /** + *
+   * 字段名:+支付者
+   * 变量名:combine_payer_info
+   * 是否必填:否
+   * 类型:object
+   * 描述:示例值:见请求示例
+   * 
+ */ + @SerializedName(value = "combine_payer_info") + private CombinePayerInfo combinePayerInfo; + + /** + *
+   * 字段名:订单金额
+   * 变量名: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 promotionDetails; + + @Data + @NoArgsConstructor + public static class SceneInfo implements Serializable { + /** + *
+     * 字段名:商户端设备号
+     * 变量名:device_id
+     * 是否必填:否
+     * 类型:string(16)
+     * 描述:
+     *  终端设备号(门店号或收银设备ID)。
+     *  特殊规则:长度最小7个字节
+     *  示例值:POS1:1
+     * 
+ */ + @SerializedName(value = "device_id") + private String deviceId; + + } + + @Data + @NoArgsConstructor + public static class CombinePayerInfo implements Serializable { + /** + *
+     * 字段名:用户标识
+     * 变量名:sp_openid
+     * 是否必填:是
+     * 类型:string(128)
+     * 描述:
+     *  用户在服务商appid下的唯一标识。
+     *  示例值:oUpF8uMuAJO_M2pxb1Q9zNjWeS6o
+     * 
+ */ + @SerializedName(value = "sp_openid") + private String spOpenid; + + + /** + *
+     * 字段名:二级商户用户标识
+     * 变量名:sub_openid
+     * 是否必填:否
+     * 类型:string(128)
+     * 描述:
+     *  用户在二级商户appid下的唯一标识。
+     *  示例值:oUpF8uMuAJO_M2pxb1Q9zNjWeS6o
+     * 
+ */ + @SerializedName(value = "sub_openid") + private String subOpenid; + + } + + @Data + @NoArgsConstructor + public static class Amount implements Serializable { + /** + *
+     * 字段名:总金额
+     * 变量名:total
+     * 是否必填:否
+     * 类型:int
+     * 描述:
+     *  订单总金额,单位为分
+     *  示例值:100
+     * 
+ */ + @SerializedName(value = "total") + private Integer total; + + + /** + *
+     * 字段名:用户支付金额
+     * 变量名:payer_total
+     * 是否必填:否
+     * 类型:int
+     * 描述:
+     *  用户支付金额,单位为分。
+     *  示例值:100
+     * 
+ */ + @SerializedName(value = "payer_total") + private Integer payerTotal; + + + /** + *
+     * 字段名:货币类型
+     * 变量名:currency
+     * 是否必填:否
+     * 类型:string(16)
+     * 描述:
+     *  CNY:人民币,境内商户号仅支持人民币。
+     *  示例值:CNY
+     * 
+ */ + @SerializedName(value = "currency") + private String currency; + + + /** + *
+     * 字段名:用户支付币种
+     * 变量名:payer_currency
+     * 是否必填:否
+     * 类型:string(8)
+     * 描述:
+     *  用户支付币种
+     *  示例值: CNY
+     * 
+ */ + @SerializedName(value = "payer_currency") + private String payerCurrency; + } + + @Data + @NoArgsConstructor + public static class PromotionDetail implements Serializable { + + /** + *
+     * 字段名:券ID
+     * 变量名:coupon_id
+     * 是否必填:是
+     * 类型:string(32)
+     * 描述: 券ID
+     * 示例值:109519
+     * 
+ */ + @SerializedName(value = "coupon_id") + private String couponId; + + /** + *
+     * 字段名:优惠名称
+     * 变量名:name
+     * 是否必填:否
+     * 类型:string(64)
+     * 描述: 优惠名称
+     * 示例值:单品惠-6
+     * 
+ */ + @SerializedName(value = "name") + private String name; + /** + *
+     * 字段名:优惠范围
+     * 变量名:scope
+     * 是否必填:否
+     * 类型:string(32)
+     * 描述: 优惠名称
+     * 示例值:
+     *    GLOBAL:全场代金券
+     *    SINGLE:单品优惠
+     * 示例值:GLOBAL
+     * 
+ */ + @SerializedName(value = "scope") + private String scope; + + /** + *
+     * 字段名:优惠类型
+     * 变量名:type
+     * 是否必填:否
+     * 类型:string(32)
+     * 描述:
+     *    CASH:充值
+     *    NOCASH:预充值
+     * 示例值:CASH
+     * 
+ */ + @SerializedName(value = "type") + private String type; + + /** + *
+     * 字段名:优惠券面额
+     * 变量名:amount
+     * 是否必填:是
+     * 类型:int
+     * 描述: 优惠券面额
+     * 示例值:100
+     * 
+ */ + @SerializedName(value = "amount") + private Integer amount; + + /** + *
+     * 字段名:活动ID
+     * 变量名:stock_id
+     * 是否必填:否
+     * 类型:string(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(16)
+     * 描述:
+     *    CNY:人民币,境内商户号仅支持人民币。
+     * 示例值:CNY
+     * 
+ */ + @SerializedName(value = "currency") + private String currency; + + /** + *
+     * 字段名:单品列表
+     * 变量名:goods_detail
+     * 是否必填:否
+     * 类型:array
+     * 描述:单品列表信息
+     * 
+ */ + @SerializedName(value = "goods_detail") + private List goodsDetails; + + + } + + @Data + @NoArgsConstructor + public static class GoodsDetail implements Serializable { + + /** + *
+     * 字段名:商品编码
+     * 变量名:goods_id
+     * 是否必填:是
+     * 类型:string(32)
+     * 描述:商品编码
+     * 示例值:M1006
+     * 
+ */ + @SerializedName(value = "goods_id") + private String goodsId; + + /** + *
+     * 字段名:商品数量
+     * 变量名:quantity
+     * 是否必填:是
+     * 类型:int64
+     * 描述:
+     *  用户购买的数量
+     * 示例值:1
+     * 
+ */ + @SerializedName(value = "quantity") + private Integer quantity; + + /** + *
+     * 字段名:商品单价
+     * 变量名:unit_price
+     * 是否必填:是
+     * 类型:int64
+     * 描述:
+     *  商品单价,单位为分
+     * 示例值:100
+     * 
+ */ + @SerializedName(value = "unit_price") + private Integer unitPrice; + + /** + *
+     * 字段名:商品优惠金额
+     * 变量名:discount_amount
+     * 是否必填:是
+     * 类型:int
+     * 描述:商品优惠金额
+     * 示例值:0
+     * 
+ */ + @SerializedName(value = "discount_amount") + private Integer discountAmount; + + /** + *
+     * 字段名:商品备注
+     * 变量名:goods_remark
+     * 是否必填:否
+     * 类型:string(128)
+     * 描述:商品备注信息
+     * 示例值:商品备注信息
+     * 
+ */ + @SerializedName(value = "goods_remark") + private String goodsRemark; + } + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SignatureHeader.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SignatureHeader.java new file mode 100644 index 0000000000..1becb23b40 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SignatureHeader.java @@ -0,0 +1,35 @@ +package com.github.binarywang.wxpay.bean.ecommerce; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 微信通知接口头部信息,需要做签名验证 + * 文档地址: https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/qian-ming-yan-zheng + */ +@Data +@NoArgsConstructor +public class SignatureHeader implements Serializable { + + /** + * 时间戳 + */ + private String timeStamp; + + /** + * 随机串 + */ + private String nonce; + + /** + * 已签名字符串 + */ + private String signed; + + /** + * 证书序列号 + */ + private String serialNo; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/TransactionsResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/TransactionsResult.java index 3ce288a701..99ba866cf0 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/TransactionsResult.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/TransactionsResult.java @@ -97,7 +97,7 @@ public T getPayInfo(TradeTypeEnum tradeType, String appId, String mchId, Pri //签名类型,默认为RSA,仅支持RSA。 .setSignType("RSA").setPaySign(SignUtils.sign(jsapiResult.getSignStr(), privateKey)); return (T) jsapiResult; - case H5: + case MWEB: return (T) this.h5Url; case APP: AppResult appResult = new AppResult(); diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/enums/TradeTypeEnum.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/enums/TradeTypeEnum.java index 03dd2cb36b..7c54925b66 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/enums/TradeTypeEnum.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/enums/TradeTypeEnum.java @@ -13,7 +13,7 @@ public enum TradeTypeEnum { APP("/v3/combine-transactions/app","/v3/pay/partner/transactions/app"), JSAPI("/v3/combine-transactions/jsapi","/v3/pay/partner/transactions/jsapi"), NATIVE("/v3/combine-transactions/native","/v3/pay/partner/transactions/native"), - H5("/v3/combine-transactions/h5","/v3/pay/partner/transactions/h5") + MWEB("/v3/combine-transactions/h5","/v3/pay/partner/transactions/h5") ; /** diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java index add7c17d1d..3910655a80 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java @@ -67,6 +67,18 @@ public interface EcommerceService { */ T combineTransactions(TradeTypeEnum tradeType, CombineTransactionsRequest request) throws WxPayException; + /** + *
+   * 合单支付通知回调数据处理
+   * 文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pages/e-combine.shtml
+   * 
+ * + * @param notifyData 通知数据 + * @param header 通知头部数据,不传则表示不校验头 + * @return 解密后通知数据 + */ + CombineTransactionsNotifyResult parseCombineNotifyResult(String notifyData, SignatureHeader header) throws WxPayException; + /** *
    *  服务商模式普通支付API(APP支付、JSAPI支付、H5支付、NATIVE支付).
@@ -78,4 +90,16 @@ public interface EcommerceService {
    * @return 调起支付需要的参数
    */
    T partnerTransactions(TradeTypeEnum tradeType, PartnerTransactionsRequest request) throws WxPayException;
+
+  /**
+   * 
+   * 普通支付通知回调数据处理
+   * 文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pages/e_transactions.shtml
+   * 
+ * + * @param notifyData 通知数据 + * @param header 通知头部数据,不传则表示不校验头 + * @return 解密后通知数据 + */ + PartnerTransactionsNotifyResult parsePartnerNotifyResult(String notifyData, SignatureHeader header) throws WxPayException; } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java index 45205ac06a..21cad6d690 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java @@ -2,15 +2,21 @@ import com.github.binarywang.wxpay.bean.ecommerce.*; import com.github.binarywang.wxpay.bean.ecommerce.enums.TradeTypeEnum; +import com.github.binarywang.wxpay.bean.payscore.PayScoreNotifyData; import com.github.binarywang.wxpay.exception.WxPayException; import com.github.binarywang.wxpay.service.EcommerceService; import com.github.binarywang.wxpay.service.WxPayService; +import com.github.binarywang.wxpay.v3.util.AesUtils; import com.github.binarywang.wxpay.v3.util.RsaCryptoUtil; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import lombok.RequiredArgsConstructor; +import java.io.IOException; import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; +import java.util.Objects; @RequiredArgsConstructor public class EcommerceServiceImpl implements EcommerceService { @@ -49,6 +55,27 @@ public T combineTransactions(TradeTypeEnum tradeType, CombineTransactionsReq request.getCombineMchid(), payService.getConfig().getPrivateKey()); } + @Override + public CombineTransactionsNotifyResult parseCombineNotifyResult(String notifyData, SignatureHeader header) throws WxPayException { + if(Objects.nonNull(header) && !this.verifyNotifySign(header, notifyData)){ + throw new WxPayException("非法请求,头部信息验证失败"); + } + NotifyResponse response = GSON.fromJson(notifyData, NotifyResponse.class); + NotifyResponse.Resource resource = response.getResource(); + String cipherText = resource.getCiphertext(); + String associatedData = resource.getAssociatedData(); + String nonce = resource.getNonce(); + String apiV3Key = this.payService.getConfig().getApiV3Key(); + try { + String result = AesUtils.decryptToString(associatedData, nonce,cipherText, apiV3Key); + CombineTransactionsNotifyResult notifyResult = GSON.fromJson(result, CombineTransactionsNotifyResult.class); + notifyResult.setRawData(response); + return notifyResult; + } catch (GeneralSecurityException | IOException e) { + throw new WxPayException("解析报文异常!", e); + } + } + @Override public T partnerTransactions(TradeTypeEnum tradeType, PartnerTransactionsRequest request) throws WxPayException { String url = this.payService.getPayBaseUrl() + tradeType.getPartnerUrl(); @@ -57,4 +84,34 @@ public T partnerTransactions(TradeTypeEnum tradeType, PartnerTransactionsReq return result.getPayInfo(tradeType, request.getSpAppid(), request.getSpMchid(), payService.getConfig().getPrivateKey()); } + + @Override + public PartnerTransactionsNotifyResult parsePartnerNotifyResult(String notifyData, SignatureHeader header) throws WxPayException { + if(Objects.nonNull(header) && !this.verifyNotifySign(header, notifyData)){ + throw new WxPayException("非法请求,头部信息验证失败"); + } + NotifyResponse response = GSON.fromJson(notifyData, NotifyResponse.class); + NotifyResponse.Resource resource = response.getResource(); + String cipherText = resource.getCiphertext(); + String associatedData = resource.getAssociatedData(); + String nonce = resource.getNonce(); + String apiV3Key = this.payService.getConfig().getApiV3Key(); + try { + String result = AesUtils.decryptToString(associatedData, nonce,cipherText, apiV3Key); + PartnerTransactionsNotifyResult notifyResult = GSON.fromJson(result, PartnerTransactionsNotifyResult.class); + notifyResult.setRawData(response); + return notifyResult; + } catch (GeneralSecurityException | IOException e) { + throw new WxPayException("解析报文异常!", e); + } + } + + private boolean verifyNotifySign(SignatureHeader header, String data) { + String beforeSign = String.format("%s\n%s\n%s\n", + header.getTimeStamp(), + header.getNonce(), + data); + return payService.getConfig().getVerifier().verify(header.getSerialNo(), + beforeSign.getBytes(StandardCharsets.UTF_8), header.getSigned()); + } } From c8fc670f52369f42effb066145791604c6ea3657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9B=BE=E6=B5=A9?= Date: Thu, 10 Sep 2020 11:02:47 +0800 Subject: [PATCH 8/8] =?UTF-8?q?new:=E7=94=B5=E5=95=86=E6=94=B6=E4=BB=98?= =?UTF-8?q?=E9=80=9A=E6=94=AF=E4=BB=98=E5=9B=9E=E8=B0=83=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../binarywang/wxpay/service/impl/EcommerceServiceImpl.java | 1 - 1 file changed, 1 deletion(-) diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java index 21cad6d690..cf4f42ed65 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java @@ -2,7 +2,6 @@ import com.github.binarywang.wxpay.bean.ecommerce.*; import com.github.binarywang.wxpay.bean.ecommerce.enums.TradeTypeEnum; -import com.github.binarywang.wxpay.bean.payscore.PayScoreNotifyData; import com.github.binarywang.wxpay.exception.WxPayException; import com.github.binarywang.wxpay.service.EcommerceService; import com.github.binarywang.wxpay.service.WxPayService;