diff --git a/.gitignore b/.gitignore index 6e48406..c5693ed 100644 --- a/.gitignore +++ b/.gitignore @@ -60,3 +60,4 @@ org.eclipse.jdt.core.prefs example/out .vscode .classpath +target/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 1148b23..b5b94ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,15 +1,55 @@ # ChangeLog +## 2.5.7 + +- 新增 TradeDetail 交易详情接口 +- 下线SubBank、Contact、UserPic 接口 + +## 2.5.6 + +- 更新: `RequestOptions` 新增`Ping++`验签公钥设置参数 + +## 2.5.5 + +-更新: 退款 refund 新增返回 `app` 参数. + +## 2.5.4 + +- 修正: 修复版本 2.5.3 中存在的 POM 依赖问题。建议用户直接升级到此版本。 + +## 2.5.3 + +- 新增: 签约修改接口 + +## 2.5.2 + +- 修改: 正确 `Map` 以及 `List` 内的元素类型 + +## 2.5.0 + +- 新增: 响应签名验签 +- 新增: 微信扣款预扣费通知实现 +- 更新: `RequestOptions` 新增`Ping++`公钥设置参数 +- 更新: webhook验签方法移动至 `PingppSignature` 类中 +- 更新: `gson` 到`2.10`;`commons-codec` 到 `1.17.0` + +## 2.4.1 + +- 修改: Refund 对象新增`currency` 字段 +- 修改: 更新示例 Main.java 中设置私钥和私钥文件地址写法 +- 修改: build.gradle 镜像仓库使用阿里云源 + ## 2.4.0 > 本次更新有较多与旧版本不兼容的地方。 -- 修改: 支持 JDK 8 及以上; +- 修改: 支持 JDK 8 及以上; - 修改: 异常抛出修改,统一声明为 `PingppException`,实际使用可以再做具体判断; - 废弃: 原接口,支持传 `apiKey` 参数的方法,全部删除,可以用 `RequestOptions` 代替; - 新增: 每个接口最后一个参数支持 `RequestOptions`; - 新增: `PingppException` 增加 `type`、`code`、`statusCode`; -- 新增: 对象增加 `getLastResponse()` 方法,可以用于获取当前这次请求的 `response` 对象,包含 `HTTP Status Code`、`Headers`、`Response Body` 等; +- 新增: 对象增加 `getLastResponse()` 方法,可以用于获取当前这次请求的 `response` 对象,包含 `HTTP Status Code`、`Headers`、 + `Response Body` 等; - 废弃: 取消对 `PKCS1` 格式私钥的支持,请转成 `PKCS8` 再使用; --- @@ -60,25 +100,34 @@ - 移除 `PingppAccount` ## 2.3.8 (2018-05-22) + #### 新增 + - WxLiteOAuth 接口 - Agreement 接口 #### 修改 + - 合并部分内部类 - WxpubOAuth 接口错误时增加异常 ## 2.3.7 (2018-03-16) + #### 新增 + - BalanceSettlement 接口 - CardInfo 接口 ## 2.3.5 (2018-02-27) + #### 修正 + - 部分对象字段从 Integer 改成 Long ## 2.3.4 (2018-01-10) + #### 修改 + - 移除已废弃接口 - 移除 transfer、batch_transfer 取消接口 - 更新示例 @@ -86,137 +135,190 @@ - settle_account 字段更新 ## 2.3.3 (2017-12-27) + #### 修正 + - 修正 javadoc ## 2.3.2 (2017-12-21) + #### 修正 + - 兼容 JDK 1.7 ## 2.3.1 (2017-12-08) + #### 修正 + - Event 解析修正 ## 2.3.0 (2017-12-01) + #### 更改 + - 合并账户系统相关接口 ## 2.2.5 (2017-10-10) + #### 新增 + - 新增重试机制 #### 修改 + - 优化 SDK ## 2.2.4 (2017-06-19) + #### 修改 + - Charge 增加字段 reversed,表示是否已撤销 - 针对渠道 isv_scan, isv_qr_isv_wap 增加撤销方法,`Charge.reverse(CHARGE_ID)` - Refund 增加 extra 字段 ## 2.2.3 (2017-03-27) + #### 修改 + - BatchTransfer 增加字段 ## 2.2.2 (2017-03-21) + #### 修正 + - alipay_pc_direct/alipay_wap 渠道 credential 字段类型的修正 ## 2.2.1 (2017-01-13) + #### 新增 + - 添加 gradle 相关文件 - 添加部分测试文件 - batch_transfer 更新/取消接口 #### 修改 + - 更改签名私钥获取方式 - Charge.list app[id] 参数改为必传 - 红包去除多余的参数 - batch_refund charges 字段格式修改 #### 修正 + - Webhooks 解析对象 batch_refund、batch_transfer、customs 对应事件错误 ## 2.2.0 + #### 新增 + - 添加 BatchRefund、BatchTransfer、Customs #### 修改 + - 签名生成规则变更 - 列表查询接口换成 `list` 方法,代替 `all` 方法 - 退款接口调用方式变更,直接使用 Refund 对象 - Refund 对象添加 charge_order_no, transaction_no 属性 ## 2.1.9 + #### 修改 -- 添加 RateLimitException + +- 添加 RateLimitException - 兼容 http 地址,仅为方便测试 ## 2.1.8 + #### 新增 + - Identification 身份证银行卡接口 ## 2.1.7 + #### 修正 + - RedEnvelope、Transfer 添加字段 ## 2.1.6 + #### 修正 + - 签名内容编码问题 ## 2.1.5 + #### 修改 + - 补充 RedEnvelope、Transfer 缺少字段 ## 2.1.4 + #### 修改 + - 更改 content-type 为 application/json ## 2.1.3 + - 兼容两种微信公众号接入方式 ## 2.1.2 + - 补全确实字段 - 修正 Event 解析 - 修正部分字符 JSON 序列化问题 - 添加请求签名 ## 2.1.1 + - 修正 extra 类型 - 兼容 jdk 1.6 ## 2.1.0 + - 添加应用内快捷支付对应接口 ## 2.0.10 + - 增加 Refund 对象 status 字段 ## 2.0.8 + - 增加 ChannelException ## 2.0.6 + - 增加企业转账、修改红包接口 ## 2.0.5 + - 增加 event sdk ## 2.0.4 + - 更改 sdk 目录格式,修复wx_pub渠道timeStamp 类型问题。 ## 2.0.3 + - 增加 apple pay ,重写 getcredential 方法。 ## 2.0.2 + - 增加微信红包 ## 2.0.1 + #### 更改 + 修改 wx credential 里的 timeStamp 类型为字符串,防止变成科学计数法 ## 2.0.0 + #### 更改 + 添加新渠道支持:百付宝、百付宝WAP、微信公众平台 ## 1.0.4 + #### 更改: + 添加 `Expanding` 机制 diff --git a/README.md b/README.md index 22b63f5..4efc776 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Java 要求 JDK 8 及以上。 ### 手动安装 -JAR 下载地址: [pingpp-java](https://jcenter.bintray.com/Pingplusplus/pingpp-java/) +JAR 下载地址: [pingpp-java](https://mvnrepository.com/artifact/com.pingxx/pingpp-java) 请根据版本号下载相应的 JAR 文件并导入至工程。 @@ -29,23 +29,16 @@ JAR 下载地址: [pingpp-java](https://jcenter.bintray.com/Pingplusplus/pingpp- maven 远程仓库 ``` xml - - - false - - central - bintray - https://jcenter.bintray.com - +mavenCentral ``` 安装 Ping++ SDK ``` xml - Pingplusplus + com.pingxx pingpp-java - 2.4.0 + 2.5.7 jar ``` @@ -56,16 +49,14 @@ gradle 远程仓库 ``` repositories { - maven { - url "https://jcenter.bintray.com" - } + mavenCentral() } ``` 安装 Ping++ SDK ``` -compile 'Pingplusplus:pingpp-java:2.4.0' +implementation 'com.pingxx:pingpp-java:2.5.7' ``` ## 初始化 diff --git a/VERSION b/VERSION index 197c4d5..35d16fb 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.4.0 +2.5.7 diff --git a/build.gradle b/build.gradle index 0025db6..e7c254d 100644 --- a/build.gradle +++ b/build.gradle @@ -1,85 +1,12 @@ buildscript { repositories { - jcenter() + mavenCentral() } } -plugins { - id "com.jfrog.bintray" version "1.8.0" -} - allprojects { repositories { - jcenter() + mavenCentral() } - apply plugin: 'maven' - apply plugin: 'maven-publish' apply plugin: 'java' } - -sourceCompatibility = 1.8 -targetCompatibility = 1.8 - -dependencies { - compile 'com.google.code.gson:gson:2.8.5' - compile 'commons-codec:commons-codec:1.10' - - testCompile 'junit:junit:4.12' -} - -task sourcesJar(type: Jar, dependsOn: classes) { - classifier = 'sources' - from sourceSets.main.allSource -} - -task javadocJar(type: Jar, dependsOn: javadoc) { - classifier = 'javadoc' - from javadoc.destinationDir -} - -artifacts { - archives sourcesJar, javadocJar -} - -publishing { - publications { - mavenJava(MavenPublication) { - from components.java - artifact sourcesJar - artifact javadocJar - groupId 'Pingplusplus' - artifactId 'pingpp-java' - version releaseVersion - - pom.withXml { - def root = asNode() - root.appendNode('name', 'pingpp-java') - root.appendNode('url', 'https://github.com/PingPlusPlus/pingpp-java') - } - } - } -} - -bintray { - user = bintrayUser - key = bintrayApiKey - publications = ['mavenJava'] - dryRun = false - publish = true - pkg { - repo = 'maven' - name = 'pingplusplus' - licenses = ['MIT'] - labels = ['Ping++', 'pingxx', 'payment'] - vcsUrl = 'https://github.com/PingPlusPlus/pingpp-java.git' - version { - name = releaseVersion - } - } -} - -model { - tasks.generatePomFileForMavenJavaPublication { - destination = file("pom.xml") - } -} diff --git a/docs/update/2.3-to-2.4.md b/docs/update/2.3-to-2.4.md index f8a39b8..6a2ad38 100644 --- a/docs/update/2.3-to-2.4.md +++ b/docs/update/2.3-to-2.4.md @@ -70,7 +70,6 @@ charge.refunds.list(String apiKey, Map params) charge.refunds.retrieve(String id, String apiKey) charge.refunds.create(String apiKey, Map params) -Contact.create(Map params, String apiKey) Customs.create(String apiKey, Map params) Customs.retrieve(String id, String apiKey) @@ -103,14 +102,11 @@ SplitReceiver.retrieve(String id, String apiKey) SplitReceiver.list(Map params, String apiKey) SplitReceiver.delete(String id, String apiKey) -SubBank.query(Map params, String apiKey) - Transfer.create(String apiKey, Map params) Transfer.retrieve(String id, String apiKey) Transfer.retrieve(String id, String apiKey, Map params) Transfer.list(String apiKey, Map params) -UserPic.upload(Map params, String apiKey) ``` #### 替换示例 diff --git a/example/build.gradle b/example/build.gradle index a9101ed..6b70aea 100644 --- a/example/build.gradle +++ b/example/build.gradle @@ -1,12 +1,13 @@ apply plugin: 'java' -repositories { - jcenter() -} - dependencies { - compile 'com.google.code.gson:gson:2.8.5' - compile 'commons-codec:commons-codec:1.10' + implementation 'com.google.code.gson:gson:2.10' + implementation 'commons-codec:commons-codec:1.17.0' - compile project(':') + // 方法 1. 本地文件系统依赖 + // implementation fileTree(dir: 'lib', includes: ['*jar']) + // 方法 2. 仓库依赖 + // implementation 'com.pingxx:pingpp-java:2.5.7' + // 方法 3.模块依赖 + implementation project(':pingpp-sdk') } diff --git a/example/res/pingpp_public_key.pem b/example/res/pingpp_public_key.pem deleted file mode 100644 index 7cf6d7c..0000000 --- a/example/res/pingpp_public_key.pem +++ /dev/null @@ -1,9 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzs8SiPoFQT9K0lWa6WSx -0d6UnA391KM2aFwijY0AK7r+MiAe07ivenopzFL3dqIRhQjuP7d30V85kWydN5UZ -cm/tZgm4K+8wttb988hOrzSjtPOMghHK+bnDwE8FIB+ZbHAZCEVhNfE6i9kLGbHH -Q617+mxUTJ3yEZG9CIgke475o2Blxy4UMsRYjo2gl5aanzmOmoZcbiC/R5hXSQUH -XV9/VzA7U//DIm8Xn7rerd1n8+KWCg4hrIIu/A0FKm8zyS4QwAwQO2wdzGB0h15t -uFLhjVz1W5ZPXjmCRLzTUoAvH12C6YFStvS5kjPcA66P1nSKk5o3koSxOumOs0iC -EQIDAQAB ------END PUBLIC KEY----- diff --git a/example/res/your_rsa_private_key_pkcs8.pem b/example/res/your_rsa_private_key_pkcs8.pem index e4e042c..9da76fb 100644 --- a/example/res/your_rsa_private_key_pkcs8.pem +++ b/example/res/your_rsa_private_key_pkcs8.pem @@ -1,28 +1 @@ ------BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDHYyS3FwoESp1h -GLYiBhy6k9Ag3lzGCIEvm50IIEkE0Ftc9qq44TWqyl+EHUpTMdcBOcI42JLO5stw -FOfCLa3PQStEJ4llIRFEKlsrHh67pvWd5RNaSBrvGlnFY40S+SZmjk2WF/h9dE9R -ic79t0YI0alD8dIl9Yu3OaEKo7VonBWFwOYMxjPhtORlq+EUF1XJd//yftQrKWTT -d7KaUonWzBCl4VzFop/OyTWYlTuZz3eYJaNpH5VaQ1vDgBAcPIeBvMf7NgBHMKW6 -LLmFd2LEYQ/6I7hkGTjysSzWEpO8bPWT6OEsJ2R2kFGOrSkr+G2MDcJ7ykXYAmz5 -+A3plS6ZAgMBAAECggEAVrgwR9GlcahiOtDcpn+yDxQq+aC9CQS561LrQZWJLKbS -leRS7IZHKTlLwdJbeUO8F7RfXQoVEBghc2YkRrhHWFUn1ES95VY0hElHzcET7Nn5 -CeuQNzwVOtljIg7iVNY4dXJ/HEDguu/Tb8tYU9FajItj60FJ/WiGk/JksJPzWsOC -VPVniy9fTbTLy1e+dCpCI6OXirtm7hvbodRNDjree0wSEzm7vL0wVzEZFo6kX+AB -GUwaoO7pPyH+hgyI5Iuhc65NHsHzTJpf8yNFl9QGhkxvm2Ff2oEtDt1idOTBrHB6 -tg+ti9Ctb2+2yzBnk14hsSYJnKitR7wM6ZCFPX4eYQKBgQD+JAREeFkodec/SC+G -X+4Q4Y68uMPkfUPrMKXM4cyY5wgXk64RBvRVxIxX7x6Y3tIKn9v8tWAprbsyVr15 -eb4RcAFEVwjuoZixhd9sIPsRhfdNolKn/fSPIsHL4ywcJMSIt7KVKHuQeqBNHy0o -0PxQjNej1ozsmrAWqV55cbKHswKBgQDI2JQRTPIEC/2y6LdmBVhGJW9OKWTYdVNj -q7rX+Yw4uxOtfd5hBqpvgZEklKEk72aazFdEcERlAm9SqoX09qk6zK/wcq4Xn5Q/ -qy8ecmjuyf2AK9X+HUdMerMVxhK9RpeevKYP/RO2F/wIN64anlQVYygVkXXgdOvW -hBE4YABKgwKBgDRtmbPGYB5ItHwJmERQZfx1i8zDESaB8RED6DBsJJkmkDTM8ovw -s1c+RPWfDuDalto6QFfR0xTGEmhAHLaCtwNB6AEBM4aHL8jvpTfZVfI3gN0zL3oY -mestcG1vYBouO504yE6dG2Ci6479b4OMGYFEjPfvuwLUpp8GMcc7/WihAoGANCp8 -mtm/ammq5VMof2kX+nAyrrx1ovsmQ5cRGpOIZhvBCqjMn6rZjci7aCLqj+tWXRKC -ABagzROK0o/T50JBxjHv6KYArcYW/Up7HI9ezdbM7wNzu2LjZ+veo+MkbuDs9J/P -CgwTmJI2NfQwVl2VPVDZ0nBLi5cSwk7fIiNdL/0CgYEAtECmC1QDs53Di2MIsa/F -e4sWfJGSDqEWqhcA/aPwf1skM6VJJXBBMV1qFtwgO1AlLnu9dQYra6ylsUoubVYI -XM9XK7EMhbqi57+Q75jHFTc0DnzOTyho5Gp4Ddi8dztmZGNWdWTGdeMqh+svqMXk -D6VdJeddyGu/Zlgj7Wk6whU= ------END PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDHYyS3FwoESp1hGLYiBhy6k9Ag3lzGCIEvm50IIEkE0Ftc9qq44TWqyl+EHUpTMdcBOcI42JLO5stwFOfCLa3PQStEJ4llIRFEKlsrHh67pvWd5RNaSBrvGlnFY40S+SZmjk2WF/h9dE9Ric79t0YI0alD8dIl9Yu3OaEKo7VonBWFwOYMxjPhtORlq+EUF1XJd//yftQrKWTTd7KaUonWzBCl4VzFop/OyTWYlTuZz3eYJaNpH5VaQ1vDgBAcPIeBvMf7NgBHMKW6LLmFd2LEYQ/6I7hkGTjysSzWEpO8bPWT6OEsJ2R2kFGOrSkr+G2MDcJ7ykXYAmz5+A3plS6ZAgMBAAECggEAVrgwR9GlcahiOtDcpn+yDxQq+aC9CQS561LrQZWJLKbSleRS7IZHKTlLwdJbeUO8F7RfXQoVEBghc2YkRrhHWFUn1ES95VY0hElHzcET7Nn5CeuQNzwVOtljIg7iVNY4dXJ/HEDguu/Tb8tYU9FajItj60FJ/WiGk/JksJPzWsOCVPVniy9fTbTLy1e+dCpCI6OXirtm7hvbodRNDjree0wSEzm7vL0wVzEZFo6kX+ABGUwaoO7pPyH+hgyI5Iuhc65NHsHzTJpf8yNFl9QGhkxvm2Ff2oEtDt1idOTBrHB6tg+ti9Ctb2+2yzBnk14hsSYJnKitR7wM6ZCFPX4eYQKBgQD+JAREeFkodec/SC+GX+4Q4Y68uMPkfUPrMKXM4cyY5wgXk64RBvRVxIxX7x6Y3tIKn9v8tWAprbsyVr15eb4RcAFEVwjuoZixhd9sIPsRhfdNolKn/fSPIsHL4ywcJMSIt7KVKHuQeqBNHy0o0PxQjNej1ozsmrAWqV55cbKHswKBgQDI2JQRTPIEC/2y6LdmBVhGJW9OKWTYdVNjq7rX+Yw4uxOtfd5hBqpvgZEklKEk72aazFdEcERlAm9SqoX09qk6zK/wcq4Xn5Q/qy8ecmjuyf2AK9X+HUdMerMVxhK9RpeevKYP/RO2F/wIN64anlQVYygVkXXgdOvWhBE4YABKgwKBgDRtmbPGYB5ItHwJmERQZfx1i8zDESaB8RED6DBsJJkmkDTM8ovws1c+RPWfDuDalto6QFfR0xTGEmhAHLaCtwNB6AEBM4aHL8jvpTfZVfI3gN0zL3oYmestcG1vYBouO504yE6dG2Ci6479b4OMGYFEjPfvuwLUpp8GMcc7/WihAoGANCp8mtm/ammq5VMof2kX+nAyrrx1ovsmQ5cRGpOIZhvBCqjMn6rZjci7aCLqj+tWXRKCABagzROK0o/T50JBxjHv6KYArcYW/Up7HI9ezdbM7wNzu2LjZ+veo+MkbuDs9J/PCgwTmJI2NfQwVl2VPVDZ0nBLi5cSwk7fIiNdL/0CgYEAtECmC1QDs53Di2MIsa/Fe4sWfJGSDqEWqhcA/aPwf1skM6VJJXBBMV1qFtwgO1AlLnu9dQYra6ylsUoubVYIXM9XK7EMhbqi57+Q75jHFTc0DnzOTyho5Gp4Ddi8dztmZGNWdWTGdeMqh+svqMXkD6VdJeddyGu/Zlgj7Wk6whU= \ No newline at end of file diff --git a/example/src/main/java/com/pingxx/example/AgreementExample.java b/example/src/main/java/com/pingxx/example/AgreementExample.java new file mode 100644 index 0000000..205a51c --- /dev/null +++ b/example/src/main/java/com/pingxx/example/AgreementExample.java @@ -0,0 +1,30 @@ +package com.pingxx.example; + +import com.pingplusplus.exception.PingppException; +import com.pingplusplus.model.Agreement; + +import java.util.HashMap; +import java.util.Map; + +public class AgreementExample { + + private String appId; + + public AgreementExample(String appId) { + this.appId = appId; + } + + public static void runDemos(String appId) throws PingppException { + AgreementExample example = new AgreementExample(appId); + Agreement agrModify = example.modify(); + } + + public Agreement modify() throws PingppException { + String agreementId = "agr_123456"; + Map params = new HashMap<>(); + params.put("deduct_time", "2025-07-10"); + params.put("memo", "扣款失败,需延期扣款时间"); + + return Agreement.modify(agreementId, params); + } +} diff --git a/example/src/main/java/com/pingxx/example/AgreementNotifyExample.java b/example/src/main/java/com/pingxx/example/AgreementNotifyExample.java new file mode 100644 index 0000000..54c9fa5 --- /dev/null +++ b/example/src/main/java/com/pingxx/example/AgreementNotifyExample.java @@ -0,0 +1,33 @@ +package com.pingxx.example; + +import com.pingplusplus.exception.PingppException; +import com.pingplusplus.model.AgreementNotify; + +import java.util.HashMap; +import java.util.Map; + +public class AgreementNotifyExample { + private String appId; + + AgreementNotifyExample(String appId) { + this.appId = appId; + } + + public static void runDemos(String appId) { + AgreementNotifyExample example = new AgreementNotifyExample(appId); + System.out.println("------- 签约扣款-商户预扣费通知 -------"); + example.create("agr_123456"); + } + + public AgreementNotify create(String id) { + AgreementNotify agreementNotify = null; + Map params = new HashMap(); + params.put("amount", 100); + try { + agreementNotify = AgreementNotify.create(id, params); + } catch (PingppException e) { + e.printStackTrace(); + } + return agreementNotify; + } +} diff --git a/example/src/main/java/com/pingxx/example/ChargeExample.java b/example/src/main/java/com/pingxx/example/ChargeExample.java index 88421e1..653435a 100644 --- a/example/src/main/java/com/pingxx/example/ChargeExample.java +++ b/example/src/main/java/com/pingxx/example/ChargeExample.java @@ -10,6 +10,7 @@ import com.pingplusplus.exception.PingppException; import com.pingplusplus.model.Charge; import com.pingplusplus.model.ChargeCollection; +import com.pingplusplus.model.TradeDetail; import java.util.*; @@ -107,6 +108,25 @@ public Charge retrieve(String id) { return charge; } + /** + * 查询 Charge 的渠道交易明细。 + * 该接口仅适用于已支付成功且支持交易明细查询的 Charge。 + * + * @param chargeId Charge 对象 ID + * @return 渠道交易明细 + */ + public TradeDetail retrieveTradeDetail(String chargeId) { + TradeDetail tradeDetail = null; + try { + tradeDetail = TradeDetail.retrieve(chargeId); + System.out.println(tradeDetail); + } catch (PingppException e) { + e.printStackTrace(); + } + + return tradeDetail; + } + /** * 撤销 Charge * diff --git a/example/src/main/java/com/pingxx/example/Main.java b/example/src/main/java/com/pingxx/example/Main.java index 21275d3..c38f9f2 100644 --- a/example/src/main/java/com/pingxx/example/Main.java +++ b/example/src/main/java/com/pingxx/example/Main.java @@ -3,6 +3,7 @@ import com.pingplusplus.Pingpp; import java.math.BigInteger; +import java.net.URL; import java.security.SecureRandom; /** @@ -30,6 +31,8 @@ public class Main { // 你生成的私钥路径 private final static String privateKeyFilePath = "res/your_rsa_private_key_pkcs8.pem"; + private final static String verifyKeyFilePath = "pingpp_public_key.pem"; + protected static String projectDir; public static void main(String[] args) throws Exception { @@ -41,43 +44,16 @@ public static void main(String[] args) throws Exception { Pingpp.appId = appId; // 设置私钥路径,用于请求签名 -// Pingpp.privateKeyPath = privateKeyFilePath; + //Pingpp.privateKeyPath = projectDir + privateKeyFilePath; - /** + /* * 或者直接设置私钥内容 - Pingpp.privateKey = "-----BEGIN RSA PRIVATE KEY-----\n" + - "... 私钥内容字符串 ...\n" + - "-----END RSA PRIVATE KEY-----\n"; + * Pingpp.privateKey = "... PKCS8私钥内容字符串 ..."; */ - Pingpp.privateKey = "-----BEGIN PRIVATE KEY-----\n" + - "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDHYyS3FwoESp1h\n" + - "GLYiBhy6k9Ag3lzGCIEvm50IIEkE0Ftc9qq44TWqyl+EHUpTMdcBOcI42JLO5stw\n" + - "FOfCLa3PQStEJ4llIRFEKlsrHh67pvWd5RNaSBrvGlnFY40S+SZmjk2WF/h9dE9R\n" + - "ic79t0YI0alD8dIl9Yu3OaEKo7VonBWFwOYMxjPhtORlq+EUF1XJd//yftQrKWTT\n" + - "d7KaUonWzBCl4VzFop/OyTWYlTuZz3eYJaNpH5VaQ1vDgBAcPIeBvMf7NgBHMKW6\n" + - "LLmFd2LEYQ/6I7hkGTjysSzWEpO8bPWT6OEsJ2R2kFGOrSkr+G2MDcJ7ykXYAmz5\n" + - "+A3plS6ZAgMBAAECggEAVrgwR9GlcahiOtDcpn+yDxQq+aC9CQS561LrQZWJLKbS\n" + - "leRS7IZHKTlLwdJbeUO8F7RfXQoVEBghc2YkRrhHWFUn1ES95VY0hElHzcET7Nn5\n" + - "CeuQNzwVOtljIg7iVNY4dXJ/HEDguu/Tb8tYU9FajItj60FJ/WiGk/JksJPzWsOC\n" + - "VPVniy9fTbTLy1e+dCpCI6OXirtm7hvbodRNDjree0wSEzm7vL0wVzEZFo6kX+AB\n" + - "GUwaoO7pPyH+hgyI5Iuhc65NHsHzTJpf8yNFl9QGhkxvm2Ff2oEtDt1idOTBrHB6\n" + - "tg+ti9Ctb2+2yzBnk14hsSYJnKitR7wM6ZCFPX4eYQKBgQD+JAREeFkodec/SC+G\n" + - "X+4Q4Y68uMPkfUPrMKXM4cyY5wgXk64RBvRVxIxX7x6Y3tIKn9v8tWAprbsyVr15\n" + - "eb4RcAFEVwjuoZixhd9sIPsRhfdNolKn/fSPIsHL4ywcJMSIt7KVKHuQeqBNHy0o\n" + - "0PxQjNej1ozsmrAWqV55cbKHswKBgQDI2JQRTPIEC/2y6LdmBVhGJW9OKWTYdVNj\n" + - "q7rX+Yw4uxOtfd5hBqpvgZEklKEk72aazFdEcERlAm9SqoX09qk6zK/wcq4Xn5Q/\n" + - "qy8ecmjuyf2AK9X+HUdMerMVxhK9RpeevKYP/RO2F/wIN64anlQVYygVkXXgdOvW\n" + - "hBE4YABKgwKBgDRtmbPGYB5ItHwJmERQZfx1i8zDESaB8RED6DBsJJkmkDTM8ovw\n" + - "s1c+RPWfDuDalto6QFfR0xTGEmhAHLaCtwNB6AEBM4aHL8jvpTfZVfI3gN0zL3oY\n" + - "mestcG1vYBouO504yE6dG2Ci6479b4OMGYFEjPfvuwLUpp8GMcc7/WihAoGANCp8\n" + - "mtm/ammq5VMof2kX+nAyrrx1ovsmQ5cRGpOIZhvBCqjMn6rZjci7aCLqj+tWXRKC\n" + - "ABagzROK0o/T50JBxjHv6KYArcYW/Up7HI9ezdbM7wNzu2LjZ+veo+MkbuDs9J/P\n" + - "CgwTmJI2NfQwVl2VPVDZ0nBLi5cSwk7fIiNdL/0CgYEAtECmC1QDs53Di2MIsa/F\n" + - "e4sWfJGSDqEWqhcA/aPwf1skM6VJJXBBMV1qFtwgO1AlLnu9dQYra6ylsUoubVYI\n" + - "XM9XK7EMhbqi57+Q75jHFTc0DnzOTyho5Gp4Ddi8dztmZGNWdWTGdeMqh+svqMXk\n" + - "D6VdJeddyGu/Zlgj7Wk6whU=\n" + - "-----END PRIVATE KEY-----\n"; + Pingpp.privateKey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDHYyS3FwoESp1hGLYiBhy6k9Ag3lzGCIEvm50IIEkE0Ftc9qq44TWqyl+EHUpTMdcBOcI42JLO5stwFOfCLa3PQStEJ4llIRFEKlsrHh67pvWd5RNaSBrvGlnFY40S+SZmjk2WF/h9dE9Ric79t0YI0alD8dIl9Yu3OaEKo7VonBWFwOYMxjPhtORlq+EUF1XJd//yftQrKWTTd7KaUonWzBCl4VzFop/OyTWYlTuZz3eYJaNpH5VaQ1vDgBAcPIeBvMf7NgBHMKW6LLmFd2LEYQ/6I7hkGTjysSzWEpO8bPWT6OEsJ2R2kFGOrSkr+G2MDcJ7ykXYAmz5+A3plS6ZAgMBAAECggEAVrgwR9GlcahiOtDcpn+yDxQq+aC9CQS561LrQZWJLKbSleRS7IZHKTlLwdJbeUO8F7RfXQoVEBghc2YkRrhHWFUn1ES95VY0hElHzcET7Nn5CeuQNzwVOtljIg7iVNY4dXJ/HEDguu/Tb8tYU9FajItj60FJ/WiGk/JksJPzWsOCVPVniy9fTbTLy1e+dCpCI6OXirtm7hvbodRNDjree0wSEzm7vL0wVzEZFo6kX+ABGUwaoO7pPyH+hgyI5Iuhc65NHsHzTJpf8yNFl9QGhkxvm2Ff2oEtDt1idOTBrHB6tg+ti9Ctb2+2yzBnk14hsSYJnKitR7wM6ZCFPX4eYQKBgQD+JAREeFkodec/SC+GX+4Q4Y68uMPkfUPrMKXM4cyY5wgXk64RBvRVxIxX7x6Y3tIKn9v8tWAprbsyVr15eb4RcAFEVwjuoZixhd9sIPsRhfdNolKn/fSPIsHL4ywcJMSIt7KVKHuQeqBNHy0o0PxQjNej1ozsmrAWqV55cbKHswKBgQDI2JQRTPIEC/2y6LdmBVhGJW9OKWTYdVNjq7rX+Yw4uxOtfd5hBqpvgZEklKEk72aazFdEcERlAm9SqoX09qk6zK/wcq4Xn5Q/qy8ecmjuyf2AK9X+HUdMerMVxhK9RpeevKYP/RO2F/wIN64anlQVYygVkXXgdOvWhBE4YABKgwKBgDRtmbPGYB5ItHwJmERQZfx1i8zDESaB8RED6DBsJJkmkDTM8ovws1c+RPWfDuDalto6QFfR0xTGEmhAHLaCtwNB6AEBM4aHL8jvpTfZVfI3gN0zL3oYmestcG1vYBouO504yE6dG2Ci6479b4OMGYFEjPfvuwLUpp8GMcc7/WihAoGANCp8mtm/ammq5VMof2kX+nAyrrx1ovsmQ5cRGpOIZhvBCqjMn6rZjci7aCLqj+tWXRKCABagzROK0o/T50JBxjHv6KYArcYW/Up7HI9ezdbM7wNzu2LjZ+veo+MkbuDs9J/PCgwTmJI2NfQwVl2VPVDZ0nBLi5cSwk7fIiNdL/0CgYEAtECmC1QDs53Di2MIsa/Fe4sWfJGSDqEWqhcA/aPwf1skM6VJJXBBMV1qFtwgO1AlLnu9dQYra6ylsUoubVYIXM9XK7EMhbqi57+Q75jHFTc0DnzOTyho5Gp4Ddi8dztmZGNWdWTGdeMqh+svqMXkD6VdJeddyGu/Zlgj7Wk6whU="; + //(可选,推荐设置)设置响应验证公钥,用于验证响应是否真实有效;不设置或设置密钥错误将不验签 + Pingpp.setVerifyPublicKeyPath(getResPath(verifyKeyFilePath)); // Charge 示例 ChargeExample.runDemos(appId); @@ -110,16 +86,27 @@ public static void main(String[] args) throws Exception { // 报关 // 请使用 live key 调用该接口 CustomsExample.runDemos(appId); + + //签约代扣示例 + AgreementExample.runDemos(appId); } - private static SecureRandom random = new SecureRandom(); + private static final SecureRandom random = new SecureRandom(); public static String randomString(int length) { String str = new BigInteger(130, random).toString(32); return str.substring(0, length); } + public static String getResPath(String subPath) { + if (subPath == null) { + throw new NullPointerException(); + } + URL resource = Thread.currentThread().getContextClassLoader().getResource(subPath); + return resource != null ? resource.getPath() : subPath; + } + public static int currentTimeSeconds() { - return (int)(System.currentTimeMillis() / 1000); + return (int) (System.currentTimeMillis() / 1000); } } diff --git a/example/src/main/java/com/pingxx/example/WebhooksVerifyExample.java b/example/src/main/java/com/pingxx/example/WebhooksVerifyExample.java index 31af3fe..891e1ed 100644 --- a/example/src/main/java/com/pingxx/example/WebhooksVerifyExample.java +++ b/example/src/main/java/com/pingxx/example/WebhooksVerifyExample.java @@ -7,37 +7,25 @@ */ package com.pingxx.example; -import java.io.*; -import java.security.InvalidKeyException; -import java.security.KeyFactory; -import java.security.NoSuchAlgorithmException; -import java.security.PublicKey; -import java.security.Signature; -import java.security.SignatureException; -import java.security.spec.X509EncodedKeySpec; - -import org.apache.commons.codec.binary.Base64; +import com.pingplusplus.Pingpp; +import com.pingplusplus.net.APIResource; +import com.pingplusplus.util.PingppSignature; /** - * Created by sunkai on 15/5/19. webhooks 验证签名示例 - * + * webhooks 验证签名示例 + *

* 该实例演示如何对 Ping++ webhooks 通知进行验证。 * 验证是为了让开发者确认该通知来自 Ping++ ,防止恶意伪造通知。用户如果有别的验证机制,可以不进行验证签名。 - * + *

* 验证签名需要 签名、公钥、验证信息,该实例采用文件存储方式进行演示。 * 实际项目中,需要用户从异步通知的 HTTP header 中读取签名,从 HTTP body 中读取验证信息。公钥的存储方式也需要用户自行设定。 - * - * 该实例仅供演示如何验证签名,请务必不要直接 copy 到实际项目中使用。 - * + *

+ * 该实例仅供演示如何验证签名,请务必不要直接 copy 到实际项目中使用。 */ public class WebhooksVerifyExample { - - private static String pubKeyPath = "res/pingpp_public_key.pem"; - private static String eventPath = "res/webhooks_raw_post_data.json"; - private static String signPath = "res/signature.txt"; - /** * 验证 webhooks 签名,仅供参考 + * * @param args * @throws Exception */ @@ -46,78 +34,17 @@ public static void main(String[] args) throws Exception { } public static void runDemos() throws Exception { + String verifyPublicKey = Pingpp.verifyPublicKey; // 该数据请从 request 中获取原始 POST 请求数据, 以下仅作为示例 - String webhooksRawPostData = getStringFromFile(eventPath); + String webhooksRawPostData = "{\"id\":\"evt_400240816160755820469807\",\"created\":1723795675,\"livemode\":false,\"type\":\"charge.succeeded\",\"data\":{\"object\":{\"id\":\"ch_100240816592822384640019\",\"object\":\"charge\",\"created\":1723795492,\"livemode\":false,\"paid\":true,\"refunded\":false,\"reversed\":false,\"app\":\"app_eTC8yLuj9GSSL0yv\",\"channel\":\"wx_pub\",\"order_no\":\"1723795491985uqclqj7\",\"client_ip\":\"127.0.0.1\",\"amount\":100,\"amount_settle\":100,\"currency\":\"cny\",\"subject\":\"Your Subject\",\"body\":\"Your Body\",\"extra\":{\"open_id\":\"o7xEMsySBFG3MVHI-9VsAJX-j50W\",\"limit_pay\":\"no_credit\",\"bank_type\":\"your bank type\"},\"time_paid\":1723795675,\"time_expire\":1723802692,\"time_settle\":null,\"transaction_no\":\"1290362656202408163243920572\",\"refunds\":{\"object\":\"list\",\"url\":\"/v1/charges/ch_100240816592822384640019/refunds\",\"has_more\":false,\"data\":[]},\"amount_refunded\":0,\"failure_code\":null,\"failure_msg\":null,\"metadata\":{},\"credential\":{},\"description\":null}},\"object\":\"event\",\"request\":\"iar_mrrvj9ivPOaHPOi9aLPWPqvT\",\"pending_webhooks\":0}"; System.out.println("------- POST 原始数据 -------"); System.out.println(webhooksRawPostData); // 签名数据请从 request 的 header 中获取, key 为 X-Pingplusplus-Signature (请忽略大小写, 建议自己做格式化) - String signature = getStringFromFile(signPath); + String signature = "Ju5ItDM9Hblkm6Pbb/r3G9iM58oCtAUJlJDrOud0E23TUtGBxKYJSMcuTFbPgLGLKy5QHYovJJojjMjM1CMGCp82F+SOY1U2zuwzkAk0lVqhfQk+CWGpcyUzOtLOOblKidyuIb+axZDTIg4kK/JoOR2xMH3+cJD9BN6rtfzEDbHqyZIfv6n3y/LdZNhsXfQq+qoIRuLdHrFexk1USgk7SXFvH1pCOIt8o2+dryp/ixlNj1vGq57eE8sbEzPo72vxLtzPazlgR+67cS1bUxjzObh7YFbSSTJxkHDEIi3ipTpoiI7Kc5ng7EzsWgp3cAl7on5HztfFLfT8+Bjpdvtr6w=="; System.out.println("------- 签名 -------"); System.out.println(signature); - boolean result = verifyData(webhooksRawPostData, signature, getPubKey()); + boolean result = PingppSignature.verify(signature, webhooksRawPostData, verifyPublicKey, APIResource.CHARSET.name()); System.out.println("验签结果:" + (result ? "通过" : "失败")); } - - /** - * 读取文件, 部署 web 程序的时候, 签名和验签内容需要从 request 中获得 - * @param filePath - * @return - * @throws Exception - */ - public static String getStringFromFile(String filePath) throws Exception { - FileInputStream in = new FileInputStream(Main.projectDir + filePath); - InputStreamReader inReader = new InputStreamReader(in, "UTF-8"); - BufferedReader bf = new BufferedReader(inReader); - StringBuilder sb = new StringBuilder(); - String line; - do { - line = bf.readLine(); - if (line != null) { - if (sb.length() != 0) { - sb.append("\n"); - } - sb.append(line); - } - } while (line != null); - - return sb.toString(); - } - - /** - * 获得公钥 - * @return - * @throws Exception - */ - public static PublicKey getPubKey() throws Exception { - String pubKeyString = getStringFromFile(pubKeyPath); - pubKeyString = pubKeyString.replaceAll("(-+BEGIN PUBLIC KEY-+\\r?\\n|-+END PUBLIC KEY-+\\r?\\n?)", ""); - byte[] keyBytes = Base64.decodeBase64(pubKeyString.getBytes("UTF-8")); - - // generate public key - X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes); - KeyFactory keyFactory = KeyFactory.getInstance("RSA"); - - return keyFactory.generatePublic(spec); - } - - /** - * 验证签名 - * @param dataString - * @param signatureString - * @param publicKey - * @return - * @throws NoSuchAlgorithmException - * @throws InvalidKeyException - * @throws SignatureException - */ - public static boolean verifyData(String dataString, String signatureString, PublicKey publicKey) - throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, UnsupportedEncodingException { - byte[] signatureBytes = Base64.decodeBase64(signatureString); - Signature signature = Signature.getInstance("SHA256withRSA"); - signature.initVerify(publicKey); - signature.update(dataString.getBytes("UTF-8")); - return signature.verify(signatureBytes); - } - } diff --git a/example/src/main/resources/pingpp_public_key.pem b/example/src/main/resources/pingpp_public_key.pem new file mode 100644 index 0000000..c0f5d19 --- /dev/null +++ b/example/src/main/resources/pingpp_public_key.pem @@ -0,0 +1,6 @@ +-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDCJqcTaGUab0vBZSkGNE1AaGiY +RcbPd7QdCQuM8EpbEpEQGr9/NjbJHCLd3eaUmYM0vy34Wux+VHnw4ckiDfBuW6/Z +fIo5GBRCI/nZLOnWcOX98ZAXYnn/VVJXuBtHtHL8DWEbYmMp+Gl4k1rFSuQKk52k +YLXeFJZJV8CBV5jA+wIDAQAB +-----END PUBLIC KEY----- \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 7b83409..68566cb 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.1-all.zip +distributionUrl=https\://mirrors.aliyun.com/macports/distfiles/gradle/gradle-8.6-all.zip diff --git a/libs/pingpp-sdk-2.5.3.jar b/libs/pingpp-sdk-2.5.3.jar new file mode 100644 index 0000000..72f00e6 Binary files /dev/null and b/libs/pingpp-sdk-2.5.3.jar differ diff --git a/pingpp-sdk/build.gradle b/pingpp-sdk/build.gradle new file mode 100644 index 0000000..cd44911 --- /dev/null +++ b/pingpp-sdk/build.gradle @@ -0,0 +1,52 @@ +buildscript { + repositories { + maven { + url 'https://maven.aliyun.com/repository/central' + } + mavenLocal() + mavenCentral() + } +} + +plugins { + id "maven-publish" + id "java" +} + +sourceCompatibility = 1.8 +targetCompatibility = 1.8 + +configurations { + compileOnly { + extendsFrom annotationProcessor + } + testCompileOnly { + extendsFrom testAnnotationProcessor + } +} + +dependencies { + implementation 'com.google.code.gson:gson:2.10' + implementation 'commons-codec:commons-codec:1.17.0' + testImplementation 'junit:junit:4.13.2' +} + +java { + withSourcesJar() +} + +publishing { + publications { + mavenJava(MavenPublication) { + from components.java + versionMapping { + usage('java-api') { + fromResolutionOf('runtimeClasspath') + } + usage('java-runtime') { + fromResolutionResult() + } + } + } + } +} \ No newline at end of file diff --git a/pingpp-sdk/pom.xml b/pingpp-sdk/pom.xml new file mode 100644 index 0000000..e5f7400 --- /dev/null +++ b/pingpp-sdk/pom.xml @@ -0,0 +1,123 @@ + + + 4.0.0 + + com.pingxx + pingpp-java + 2.5.7 + jar + + pingpp-java + A Java SDK for Ping++ Payment API. + https://github.com/PingPlusPlus/pingpp-java + + + Apache License 2.0 + http://www.apache.org/licenses/LICENSE-2.0 + repo + + + + + scm:git:https://github.com/PingPlusPlus/pingpp-java.git + + + + afon + Afon + xufeng.weng@pingxx.com + + + + + 1.8 + 1.8 + + + + + com.google.code.gson + gson + 2.10.1 + compile + + + commons-codec + commons-codec + 1.17.0 + compile + + + junit + junit + 4.13.2 + test + + + + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.3.1 + + + attach-sources + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.11.2 + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 3.2.8 + + + sign-artifacts + verify + + sign + + + ${gpg.keyname} + ${gpg.passphrase} + + --pinentry-mode + loopback + + + + + + + org.sonatype.central + central-publishing-maven-plugin + 0.8.0 + true + + central + true + + + + + diff --git a/src/main/java/com/pingplusplus/Pingpp.java b/pingpp-sdk/src/main/java/com/pingplusplus/Pingpp.java similarity index 79% rename from src/main/java/com/pingplusplus/Pingpp.java rename to pingpp-sdk/src/main/java/com/pingplusplus/Pingpp.java index 334ad51..7ab1f3c 100644 --- a/src/main/java/com/pingplusplus/Pingpp.java +++ b/pingpp-sdk/src/main/java/com/pingplusplus/Pingpp.java @@ -1,5 +1,11 @@ package com.pingplusplus; +import com.pingplusplus.net.APIResource; +import com.pingplusplus.util.StreamUtils; + +import java.io.FileInputStream; +import java.io.IOException; + /** * Ping++ Base class */ @@ -11,7 +17,7 @@ public abstract class Pingpp { /** * version */ - public static final String VERSION = "2.4.0"; + public static final String VERSION = "2.5.7"; /** * api key */ @@ -24,6 +30,7 @@ public abstract class Pingpp { private static volatile String apiBase = LIVE_API_BASE; public static volatile String privateKey; + public static volatile String verifyPublicKey; public static volatile String privateKeyPath; public static Boolean DEBUG = false; @@ -99,7 +106,6 @@ public static int getReadTimeout() { /** * 设置数据读取超时时间 (毫秒) - * * 不同接口的耗时时间不一样,部分接口的耗时可能比较长。 * * @param timeout timeout value in milliseconds @@ -133,4 +139,23 @@ public static String getAcceptLanguage() { public static void setAcceptLanguage(String acceptLanguage) { Pingpp.acceptLanguage = acceptLanguage; } + + public static void setVerifyPublicKeyPath(String keyFilePath) throws IOException { + Pingpp.verifyPublicKey = readKeyFromFilePath(keyFilePath); + } + + /** + * 读取密钥文件 + * + * @param filePath 文件路径 + * @return 密钥内容 + * @throws IOException IO 异常 + */ + private static String readKeyFromFilePath(String filePath) throws IOException { + FileInputStream inputStream = new FileInputStream(filePath); + String privateKey = StreamUtils.readToEnd(inputStream, APIResource.CHARSET); + inputStream.close(); + + return privateKey; + } } diff --git a/src/main/java/com/pingplusplus/exception/APIConnectionException.java b/pingpp-sdk/src/main/java/com/pingplusplus/exception/APIConnectionException.java similarity index 100% rename from src/main/java/com/pingplusplus/exception/APIConnectionException.java rename to pingpp-sdk/src/main/java/com/pingplusplus/exception/APIConnectionException.java diff --git a/src/main/java/com/pingplusplus/exception/APIException.java b/pingpp-sdk/src/main/java/com/pingplusplus/exception/APIException.java similarity index 100% rename from src/main/java/com/pingplusplus/exception/APIException.java rename to pingpp-sdk/src/main/java/com/pingplusplus/exception/APIException.java diff --git a/src/main/java/com/pingplusplus/exception/AuthenticationException.java b/pingpp-sdk/src/main/java/com/pingplusplus/exception/AuthenticationException.java similarity index 100% rename from src/main/java/com/pingplusplus/exception/AuthenticationException.java rename to pingpp-sdk/src/main/java/com/pingplusplus/exception/AuthenticationException.java diff --git a/src/main/java/com/pingplusplus/exception/ChannelException.java b/pingpp-sdk/src/main/java/com/pingplusplus/exception/ChannelException.java similarity index 100% rename from src/main/java/com/pingplusplus/exception/ChannelException.java rename to pingpp-sdk/src/main/java/com/pingplusplus/exception/ChannelException.java diff --git a/src/main/java/com/pingplusplus/exception/InvalidRequestException.java b/pingpp-sdk/src/main/java/com/pingplusplus/exception/InvalidRequestException.java similarity index 100% rename from src/main/java/com/pingplusplus/exception/InvalidRequestException.java rename to pingpp-sdk/src/main/java/com/pingplusplus/exception/InvalidRequestException.java diff --git a/src/main/java/com/pingplusplus/exception/PingppException.java b/pingpp-sdk/src/main/java/com/pingplusplus/exception/PingppException.java similarity index 100% rename from src/main/java/com/pingplusplus/exception/PingppException.java rename to pingpp-sdk/src/main/java/com/pingplusplus/exception/PingppException.java diff --git a/src/main/java/com/pingplusplus/exception/RateLimitException.java b/pingpp-sdk/src/main/java/com/pingplusplus/exception/RateLimitException.java similarity index 100% rename from src/main/java/com/pingplusplus/exception/RateLimitException.java rename to pingpp-sdk/src/main/java/com/pingplusplus/exception/RateLimitException.java diff --git a/src/main/java/com/pingplusplus/model/Agreement.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/Agreement.java similarity index 89% rename from src/main/java/com/pingplusplus/model/Agreement.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/Agreement.java index 42a6f9d..ae64880 100644 --- a/src/main/java/com/pingplusplus/model/Agreement.java +++ b/pingpp-sdk/src/main/java/com/pingplusplus/model/Agreement.java @@ -3,6 +3,7 @@ import com.pingplusplus.exception.PingppException; import com.pingplusplus.net.APIResource; import com.pingplusplus.net.RequestOptions; + import java.util.HashMap; import java.util.Map; @@ -190,6 +191,8 @@ public static Agreement retrieve(String id) throws PingppException { return retrieve(id, null); } + + /** * 查询签约(agreement) * @@ -251,4 +254,27 @@ public static Agreement cancel(String id, RequestOptions options) throws PingppE params.put("status", "canceled"); return APIResource.request(RequestMethod.PUT, instanceURL(Agreement.class, id), params, Agreement.class, options); } + + /** + * 签约修改 + * @param id + * @param params + * @param options + * @return + * @throws PingppException + */ + public static Agreement modify(String id,Map params, RequestOptions options) throws PingppException { + return APIResource.request(RequestMethod.POST, String.format("%s/%s/modify/", classURL(Agreement.class), id), params, Agreement.class, options); + } + + /** + * 签约修改 + * @param id + * @param params + * @return + * @throws PingppException + */ + public static Agreement modify(String id, Map params) throws PingppException { + return modify(id, params, null); + } } diff --git a/src/main/java/com/pingplusplus/model/AgreementCollection.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/AgreementCollection.java similarity index 100% rename from src/main/java/com/pingplusplus/model/AgreementCollection.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/AgreementCollection.java diff --git a/pingpp-sdk/src/main/java/com/pingplusplus/model/AgreementNotify.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/AgreementNotify.java new file mode 100644 index 0000000..9fd9970 --- /dev/null +++ b/pingpp-sdk/src/main/java/com/pingplusplus/model/AgreementNotify.java @@ -0,0 +1,44 @@ +package com.pingplusplus.model; + +import com.pingplusplus.exception.PingppException; +import com.pingplusplus.net.APIResource; +import com.pingplusplus.net.RequestOptions; + +import java.util.Map; + +public class AgreementNotify extends APIResource { + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public Boolean getSucceed() { + return succeed; + } + + public void setSucceed(Boolean succeed) { + this.succeed = succeed; + } + + String id; + Boolean succeed; + + public static AgreementNotify create(String id, Map params) throws PingppException { + return create(id, params, null); + } + + /** + * 签约扣款-商户预扣费通知 + * @param agreementId + * @param params + * @param options + * @return + * @throws PingppException + */ + public static AgreementNotify create(String agreementId, Map params, RequestOptions options) throws PingppException { + return APIResource.request(APIResource.RequestMethod.POST, String.format("%s/notify", instanceURL(Agreement.class, agreementId)), params, AgreementNotify.class, options); + } +} diff --git a/src/main/java/com/pingplusplus/model/App.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/App.java similarity index 100% rename from src/main/java/com/pingplusplus/model/App.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/App.java diff --git a/src/main/java/com/pingplusplus/model/BalanceBonus.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/BalanceBonus.java similarity index 100% rename from src/main/java/com/pingplusplus/model/BalanceBonus.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/BalanceBonus.java diff --git a/src/main/java/com/pingplusplus/model/BalanceBonusCollection.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/BalanceBonusCollection.java similarity index 100% rename from src/main/java/com/pingplusplus/model/BalanceBonusCollection.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/BalanceBonusCollection.java diff --git a/src/main/java/com/pingplusplus/model/BalanceSettlement.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/BalanceSettlement.java similarity index 100% rename from src/main/java/com/pingplusplus/model/BalanceSettlement.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/BalanceSettlement.java diff --git a/src/main/java/com/pingplusplus/model/BalanceSettlementCollection.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/BalanceSettlementCollection.java similarity index 100% rename from src/main/java/com/pingplusplus/model/BalanceSettlementCollection.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/BalanceSettlementCollection.java diff --git a/src/main/java/com/pingplusplus/model/BalanceTransaction.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/BalanceTransaction.java similarity index 100% rename from src/main/java/com/pingplusplus/model/BalanceTransaction.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/BalanceTransaction.java diff --git a/src/main/java/com/pingplusplus/model/BalanceTransactionCollection.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/BalanceTransactionCollection.java similarity index 100% rename from src/main/java/com/pingplusplus/model/BalanceTransactionCollection.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/BalanceTransactionCollection.java diff --git a/src/main/java/com/pingplusplus/model/BalanceTransfer.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/BalanceTransfer.java similarity index 100% rename from src/main/java/com/pingplusplus/model/BalanceTransfer.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/BalanceTransfer.java diff --git a/src/main/java/com/pingplusplus/model/BalanceTransferCollection.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/BalanceTransferCollection.java similarity index 100% rename from src/main/java/com/pingplusplus/model/BalanceTransferCollection.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/BalanceTransferCollection.java diff --git a/src/main/java/com/pingplusplus/model/BatchRefund.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/BatchRefund.java similarity index 100% rename from src/main/java/com/pingplusplus/model/BatchRefund.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/BatchRefund.java diff --git a/src/main/java/com/pingplusplus/model/BatchRefundCharges.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/BatchRefundCharges.java similarity index 100% rename from src/main/java/com/pingplusplus/model/BatchRefundCharges.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/BatchRefundCharges.java diff --git a/src/main/java/com/pingplusplus/model/BatchRefundCollection.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/BatchRefundCollection.java similarity index 100% rename from src/main/java/com/pingplusplus/model/BatchRefundCollection.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/BatchRefundCollection.java diff --git a/src/main/java/com/pingplusplus/model/BatchTransfer.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/BatchTransfer.java similarity index 100% rename from src/main/java/com/pingplusplus/model/BatchTransfer.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/BatchTransfer.java diff --git a/src/main/java/com/pingplusplus/model/BatchTransferCollection.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/BatchTransferCollection.java similarity index 100% rename from src/main/java/com/pingplusplus/model/BatchTransferCollection.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/BatchTransferCollection.java diff --git a/src/main/java/com/pingplusplus/model/BatchTransferRecipient.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/BatchTransferRecipient.java similarity index 100% rename from src/main/java/com/pingplusplus/model/BatchTransferRecipient.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/BatchTransferRecipient.java diff --git a/src/main/java/com/pingplusplus/model/BatchWithdrawal.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/BatchWithdrawal.java similarity index 100% rename from src/main/java/com/pingplusplus/model/BatchWithdrawal.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/BatchWithdrawal.java diff --git a/src/main/java/com/pingplusplus/model/BatchWithdrawalCollection.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/BatchWithdrawalCollection.java similarity index 100% rename from src/main/java/com/pingplusplus/model/BatchWithdrawalCollection.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/BatchWithdrawalCollection.java diff --git a/src/main/java/com/pingplusplus/model/CardInfo.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/CardInfo.java similarity index 100% rename from src/main/java/com/pingplusplus/model/CardInfo.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/CardInfo.java diff --git a/src/main/java/com/pingplusplus/model/Channel.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/Channel.java similarity index 100% rename from src/main/java/com/pingplusplus/model/Channel.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/Channel.java diff --git a/src/main/java/com/pingplusplus/model/Charge.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/Charge.java similarity index 100% rename from src/main/java/com/pingplusplus/model/Charge.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/Charge.java diff --git a/src/main/java/com/pingplusplus/model/ChargeCollection.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/ChargeCollection.java similarity index 100% rename from src/main/java/com/pingplusplus/model/ChargeCollection.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/ChargeCollection.java diff --git a/src/main/java/com/pingplusplus/model/ChargeEssentials.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/ChargeEssentials.java similarity index 100% rename from src/main/java/com/pingplusplus/model/ChargeEssentials.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/ChargeEssentials.java diff --git a/src/main/java/com/pingplusplus/model/ChargeRefundCollection.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/ChargeRefundCollection.java similarity index 100% rename from src/main/java/com/pingplusplus/model/ChargeRefundCollection.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/ChargeRefundCollection.java diff --git a/src/main/java/com/pingplusplus/model/Coupon.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/Coupon.java similarity index 100% rename from src/main/java/com/pingplusplus/model/Coupon.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/Coupon.java diff --git a/src/main/java/com/pingplusplus/model/CouponCollection.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/CouponCollection.java similarity index 100% rename from src/main/java/com/pingplusplus/model/CouponCollection.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/CouponCollection.java diff --git a/src/main/java/com/pingplusplus/model/CouponTemplate.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/CouponTemplate.java similarity index 100% rename from src/main/java/com/pingplusplus/model/CouponTemplate.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/CouponTemplate.java diff --git a/src/main/java/com/pingplusplus/model/CouponTemplateCollection.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/CouponTemplateCollection.java similarity index 100% rename from src/main/java/com/pingplusplus/model/CouponTemplateCollection.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/CouponTemplateCollection.java diff --git a/src/main/java/com/pingplusplus/model/CouponTemplateExpiration.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/CouponTemplateExpiration.java similarity index 100% rename from src/main/java/com/pingplusplus/model/CouponTemplateExpiration.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/CouponTemplateExpiration.java diff --git a/src/main/java/com/pingplusplus/model/Customs.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/Customs.java similarity index 100% rename from src/main/java/com/pingplusplus/model/Customs.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/Customs.java diff --git a/src/main/java/com/pingplusplus/model/CustomsCollection.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/CustomsCollection.java similarity index 100% rename from src/main/java/com/pingplusplus/model/CustomsCollection.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/CustomsCollection.java diff --git a/src/main/java/com/pingplusplus/model/DeleteRoyaltyTemplate.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/DeleteRoyaltyTemplate.java similarity index 100% rename from src/main/java/com/pingplusplus/model/DeleteRoyaltyTemplate.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/DeleteRoyaltyTemplate.java diff --git a/src/main/java/com/pingplusplus/model/DeletedChannel.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/DeletedChannel.java similarity index 100% rename from src/main/java/com/pingplusplus/model/DeletedChannel.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/DeletedChannel.java diff --git a/src/main/java/com/pingplusplus/model/DeletedCoupon.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/DeletedCoupon.java similarity index 100% rename from src/main/java/com/pingplusplus/model/DeletedCoupon.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/DeletedCoupon.java diff --git a/src/main/java/com/pingplusplus/model/DeletedCouponTemplate.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/DeletedCouponTemplate.java similarity index 100% rename from src/main/java/com/pingplusplus/model/DeletedCouponTemplate.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/DeletedCouponTemplate.java diff --git a/src/main/java/com/pingplusplus/model/DeletedObjectBase.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/DeletedObjectBase.java similarity index 100% rename from src/main/java/com/pingplusplus/model/DeletedObjectBase.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/DeletedObjectBase.java diff --git a/src/main/java/com/pingplusplus/model/DeletedPingppObject.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/DeletedPingppObject.java similarity index 100% rename from src/main/java/com/pingplusplus/model/DeletedPingppObject.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/DeletedPingppObject.java diff --git a/src/main/java/com/pingplusplus/model/DeletedSettleAccount.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/DeletedSettleAccount.java similarity index 100% rename from src/main/java/com/pingplusplus/model/DeletedSettleAccount.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/DeletedSettleAccount.java diff --git a/src/main/java/com/pingplusplus/model/DeletedSplitReceiver.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/DeletedSplitReceiver.java similarity index 100% rename from src/main/java/com/pingplusplus/model/DeletedSplitReceiver.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/DeletedSplitReceiver.java diff --git a/src/main/java/com/pingplusplus/model/DeletedSubApp.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/DeletedSubApp.java similarity index 100% rename from src/main/java/com/pingplusplus/model/DeletedSubApp.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/DeletedSubApp.java diff --git a/src/main/java/com/pingplusplus/model/Event.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/Event.java similarity index 100% rename from src/main/java/com/pingplusplus/model/Event.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/Event.java diff --git a/src/main/java/com/pingplusplus/model/EventCollection.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/EventCollection.java similarity index 100% rename from src/main/java/com/pingplusplus/model/EventCollection.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/EventCollection.java diff --git a/src/main/java/com/pingplusplus/model/EventData.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/EventData.java similarity index 100% rename from src/main/java/com/pingplusplus/model/EventData.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/EventData.java diff --git a/src/main/java/com/pingplusplus/model/Identification.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/Identification.java similarity index 100% rename from src/main/java/com/pingplusplus/model/Identification.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/Identification.java diff --git a/src/main/java/com/pingplusplus/model/Order.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/Order.java similarity index 100% rename from src/main/java/com/pingplusplus/model/Order.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/Order.java diff --git a/src/main/java/com/pingplusplus/model/OrderCollection.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/OrderCollection.java similarity index 100% rename from src/main/java/com/pingplusplus/model/OrderCollection.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/OrderCollection.java diff --git a/src/main/java/com/pingplusplus/model/OrderRefund.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/OrderRefund.java similarity index 100% rename from src/main/java/com/pingplusplus/model/OrderRefund.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/OrderRefund.java diff --git a/src/main/java/com/pingplusplus/model/OrderRefundCollection.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/OrderRefundCollection.java similarity index 100% rename from src/main/java/com/pingplusplus/model/OrderRefundCollection.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/OrderRefundCollection.java diff --git a/src/main/java/com/pingplusplus/model/PingppCollection.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/PingppCollection.java similarity index 100% rename from src/main/java/com/pingplusplus/model/PingppCollection.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/PingppCollection.java diff --git a/src/main/java/com/pingplusplus/model/PingppCollectionAPIResource.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/PingppCollectionAPIResource.java similarity index 100% rename from src/main/java/com/pingplusplus/model/PingppCollectionAPIResource.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/PingppCollectionAPIResource.java diff --git a/src/main/java/com/pingplusplus/model/PingppError.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/PingppError.java similarity index 100% rename from src/main/java/com/pingplusplus/model/PingppError.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/PingppError.java diff --git a/src/main/java/com/pingplusplus/model/PingppObject.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/PingppObject.java similarity index 100% rename from src/main/java/com/pingplusplus/model/PingppObject.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/PingppObject.java diff --git a/src/main/java/com/pingplusplus/model/PingppObjectInterface.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/PingppObjectInterface.java similarity index 100% rename from src/main/java/com/pingplusplus/model/PingppObjectInterface.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/PingppObjectInterface.java diff --git a/src/main/java/com/pingplusplus/model/PingppRawJsonObject.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/PingppRawJsonObject.java similarity index 100% rename from src/main/java/com/pingplusplus/model/PingppRawJsonObject.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/PingppRawJsonObject.java diff --git a/src/main/java/com/pingplusplus/model/ProfitTransaction.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/ProfitTransaction.java similarity index 100% rename from src/main/java/com/pingplusplus/model/ProfitTransaction.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/ProfitTransaction.java diff --git a/src/main/java/com/pingplusplus/model/ProfitTransactionCollection.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/ProfitTransactionCollection.java similarity index 100% rename from src/main/java/com/pingplusplus/model/ProfitTransactionCollection.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/ProfitTransactionCollection.java diff --git a/src/main/java/com/pingplusplus/model/Recharge.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/Recharge.java similarity index 100% rename from src/main/java/com/pingplusplus/model/Recharge.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/Recharge.java diff --git a/src/main/java/com/pingplusplus/model/RechargeCollection.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/RechargeCollection.java similarity index 100% rename from src/main/java/com/pingplusplus/model/RechargeCollection.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/RechargeCollection.java diff --git a/src/main/java/com/pingplusplus/model/RechargeRefund.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/RechargeRefund.java similarity index 100% rename from src/main/java/com/pingplusplus/model/RechargeRefund.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/RechargeRefund.java diff --git a/src/main/java/com/pingplusplus/model/RechargeRefundCollection.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/RechargeRefundCollection.java similarity index 100% rename from src/main/java/com/pingplusplus/model/RechargeRefundCollection.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/RechargeRefundCollection.java diff --git a/src/main/java/com/pingplusplus/model/RedEnvelope.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/RedEnvelope.java similarity index 100% rename from src/main/java/com/pingplusplus/model/RedEnvelope.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/RedEnvelope.java diff --git a/src/main/java/com/pingplusplus/model/RedEnvelopeCollection.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/RedEnvelopeCollection.java similarity index 100% rename from src/main/java/com/pingplusplus/model/RedEnvelopeCollection.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/RedEnvelopeCollection.java diff --git a/src/main/java/com/pingplusplus/model/Refund.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/Refund.java similarity index 95% rename from src/main/java/com/pingplusplus/model/Refund.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/Refund.java index f1145aa..9120769 100644 --- a/src/main/java/com/pingplusplus/model/Refund.java +++ b/pingpp-sdk/src/main/java/com/pingplusplus/model/Refund.java @@ -8,9 +8,11 @@ public class Refund extends APIResource { String id; + String app; String object; String orderNo; Integer amount; + String currency; Long created; Boolean succeed; String status; @@ -167,6 +169,21 @@ public void setExtra(Map extra) { this.extra = extra; } + public String getCurrency() { + return currency; + } + + public void setCurrency(String currency) { + this.currency = currency; + } + + public String getApp() { + return app; + } + + public void setApp(String app) { + this.app = app; + } /** * 创建 refund * diff --git a/src/main/java/com/pingplusplus/model/Royalty.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/Royalty.java similarity index 100% rename from src/main/java/com/pingplusplus/model/Royalty.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/Royalty.java diff --git a/src/main/java/com/pingplusplus/model/RoyaltyCollection.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/RoyaltyCollection.java similarity index 100% rename from src/main/java/com/pingplusplus/model/RoyaltyCollection.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/RoyaltyCollection.java diff --git a/src/main/java/com/pingplusplus/model/RoyaltyDataResult.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/RoyaltyDataResult.java similarity index 100% rename from src/main/java/com/pingplusplus/model/RoyaltyDataResult.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/RoyaltyDataResult.java diff --git a/src/main/java/com/pingplusplus/model/RoyaltySettlement.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/RoyaltySettlement.java similarity index 100% rename from src/main/java/com/pingplusplus/model/RoyaltySettlement.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/RoyaltySettlement.java diff --git a/src/main/java/com/pingplusplus/model/RoyaltySettlementCollection.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/RoyaltySettlementCollection.java similarity index 100% rename from src/main/java/com/pingplusplus/model/RoyaltySettlementCollection.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/RoyaltySettlementCollection.java diff --git a/src/main/java/com/pingplusplus/model/RoyaltyTemplate.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/RoyaltyTemplate.java similarity index 100% rename from src/main/java/com/pingplusplus/model/RoyaltyTemplate.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/RoyaltyTemplate.java diff --git a/src/main/java/com/pingplusplus/model/RoyaltyTemplateCollection.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/RoyaltyTemplateCollection.java similarity index 100% rename from src/main/java/com/pingplusplus/model/RoyaltyTemplateCollection.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/RoyaltyTemplateCollection.java diff --git a/src/main/java/com/pingplusplus/model/RoyaltyTransaction.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/RoyaltyTransaction.java similarity index 100% rename from src/main/java/com/pingplusplus/model/RoyaltyTransaction.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/RoyaltyTransaction.java diff --git a/src/main/java/com/pingplusplus/model/RoyaltyTransactionCollection.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/RoyaltyTransactionCollection.java similarity index 100% rename from src/main/java/com/pingplusplus/model/RoyaltyTransactionCollection.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/RoyaltyTransactionCollection.java diff --git a/src/main/java/com/pingplusplus/model/SettleAccount.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/SettleAccount.java similarity index 100% rename from src/main/java/com/pingplusplus/model/SettleAccount.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/SettleAccount.java diff --git a/src/main/java/com/pingplusplus/model/SettleAccountCollection.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/SettleAccountCollection.java similarity index 100% rename from src/main/java/com/pingplusplus/model/SettleAccountCollection.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/SettleAccountCollection.java diff --git a/src/main/java/com/pingplusplus/model/SettleAccountRecipient.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/SettleAccountRecipient.java similarity index 100% rename from src/main/java/com/pingplusplus/model/SettleAccountRecipient.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/SettleAccountRecipient.java diff --git a/src/main/java/com/pingplusplus/model/SplitProfit.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/SplitProfit.java similarity index 100% rename from src/main/java/com/pingplusplus/model/SplitProfit.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/SplitProfit.java diff --git a/src/main/java/com/pingplusplus/model/SplitProfitCollection.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/SplitProfitCollection.java similarity index 100% rename from src/main/java/com/pingplusplus/model/SplitProfitCollection.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/SplitProfitCollection.java diff --git a/src/main/java/com/pingplusplus/model/SplitProfitRecipient.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/SplitProfitRecipient.java similarity index 100% rename from src/main/java/com/pingplusplus/model/SplitProfitRecipient.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/SplitProfitRecipient.java diff --git a/src/main/java/com/pingplusplus/model/SplitReceiver.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/SplitReceiver.java similarity index 100% rename from src/main/java/com/pingplusplus/model/SplitReceiver.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/SplitReceiver.java diff --git a/src/main/java/com/pingplusplus/model/SplitReceiverCollection.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/SplitReceiverCollection.java similarity index 100% rename from src/main/java/com/pingplusplus/model/SplitReceiverCollection.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/SplitReceiverCollection.java diff --git a/src/main/java/com/pingplusplus/model/SubApp.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/SubApp.java similarity index 100% rename from src/main/java/com/pingplusplus/model/SubApp.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/SubApp.java diff --git a/src/main/java/com/pingplusplus/model/SubAppCollection.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/SubAppCollection.java similarity index 100% rename from src/main/java/com/pingplusplus/model/SubAppCollection.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/SubAppCollection.java diff --git a/src/main/java/com/pingplusplus/model/Summary.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/Summary.java similarity index 100% rename from src/main/java/com/pingplusplus/model/Summary.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/Summary.java diff --git a/pingpp-sdk/src/main/java/com/pingplusplus/model/TradeDetail.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/TradeDetail.java new file mode 100644 index 0000000..ad1cdf4 --- /dev/null +++ b/pingpp-sdk/src/main/java/com/pingplusplus/model/TradeDetail.java @@ -0,0 +1,134 @@ +package com.pingplusplus.model; + +import com.pingplusplus.exception.PingppException; +import com.pingplusplus.net.APIResource; +import com.pingplusplus.net.RequestOptions; + +/** + * Charge 的渠道交易明细。 + */ +public class TradeDetail extends APIResource { + String id; + String app; + String object; + Boolean livemode; + String channel; + String currency; + String orderNo; + String transactionNo; + Integer amount; + Integer fee; + Integer actualAmount; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getApp() { + return app; + } + + public void setApp(String app) { + this.app = app; + } + + public String getObject() { + return object; + } + + public void setObject(String object) { + this.object = object; + } + + public Boolean getLivemode() { + return livemode; + } + + public void setLivemode(Boolean livemode) { + this.livemode = livemode; + } + + public String getChannel() { + return channel; + } + + public void setChannel(String channel) { + this.channel = channel; + } + + public String getCurrency() { + return currency; + } + + public void setCurrency(String currency) { + this.currency = currency; + } + + public String getOrderNo() { + return orderNo; + } + + public void setOrderNo(String orderNo) { + this.orderNo = orderNo; + } + + public String getTransactionNo() { + return transactionNo; + } + + public void setTransactionNo(String transactionNo) { + this.transactionNo = transactionNo; + } + + public Integer getAmount() { + return amount; + } + + public void setAmount(Integer amount) { + this.amount = amount; + } + + public Integer getFee() { + return fee; + } + + public void setFee(Integer fee) { + this.fee = fee; + } + + public Integer getActualAmount() { + return actualAmount; + } + + public void setActualAmount(Integer actualAmount) { + this.actualAmount = actualAmount; + } + + /** + * 查询 Charge 的渠道交易明细。 + * + * @param chargeId Charge 对象 ID + * @return 渠道交易明细 + * @throws PingppException 请求失败时抛出 + */ + public static TradeDetail retrieve(String chargeId) throws PingppException { + return retrieve(chargeId, null); + } + + /** + * 查询 Charge 的渠道交易明细。 + * + * @param chargeId Charge 对象 ID + * @param options 本次请求使用的配置 + * @return 渠道交易明细 + * @throws PingppException 请求失败时抛出 + */ + public static TradeDetail retrieve(String chargeId, RequestOptions options) throws PingppException { + String url = String.format("%s/trade_detail", instanceURL(Charge.class, chargeId)); + return APIResource.request(APIResource.RequestMethod.GET, url, null, TradeDetail.class, options); + } +} diff --git a/src/main/java/com/pingplusplus/model/Transfer.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/Transfer.java similarity index 100% rename from src/main/java/com/pingplusplus/model/Transfer.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/Transfer.java diff --git a/src/main/java/com/pingplusplus/model/TransferCollection.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/TransferCollection.java similarity index 100% rename from src/main/java/com/pingplusplus/model/TransferCollection.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/TransferCollection.java diff --git a/src/main/java/com/pingplusplus/model/User.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/User.java similarity index 100% rename from src/main/java/com/pingplusplus/model/User.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/User.java diff --git a/src/main/java/com/pingplusplus/model/UserCollection.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/UserCollection.java similarity index 100% rename from src/main/java/com/pingplusplus/model/UserCollection.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/UserCollection.java diff --git a/src/main/java/com/pingplusplus/model/Webhooks.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/Webhooks.java similarity index 100% rename from src/main/java/com/pingplusplus/model/Webhooks.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/Webhooks.java diff --git a/src/main/java/com/pingplusplus/model/Withdrawal.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/Withdrawal.java similarity index 100% rename from src/main/java/com/pingplusplus/model/Withdrawal.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/Withdrawal.java diff --git a/src/main/java/com/pingplusplus/model/WithdrawalCollection.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/WithdrawalCollection.java similarity index 100% rename from src/main/java/com/pingplusplus/model/WithdrawalCollection.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/WithdrawalCollection.java diff --git a/src/main/java/com/pingplusplus/model/WithdrawalCollectionBase.java b/pingpp-sdk/src/main/java/com/pingplusplus/model/WithdrawalCollectionBase.java similarity index 100% rename from src/main/java/com/pingplusplus/model/WithdrawalCollectionBase.java rename to pingpp-sdk/src/main/java/com/pingplusplus/model/WithdrawalCollectionBase.java diff --git a/src/main/java/com/pingplusplus/net/APIResource.java b/pingpp-sdk/src/main/java/com/pingplusplus/net/APIResource.java similarity index 95% rename from src/main/java/com/pingplusplus/net/APIResource.java rename to pingpp-sdk/src/main/java/com/pingplusplus/net/APIResource.java index aa7b48f..1fa85f5 100644 --- a/src/main/java/com/pingplusplus/net/APIResource.java +++ b/pingpp-sdk/src/main/java/com/pingplusplus/net/APIResource.java @@ -3,9 +3,9 @@ import com.google.gson.*; import com.pingplusplus.Pingpp; import com.pingplusplus.exception.*; -import com.pingplusplus.exception.InvalidRequestException; import com.pingplusplus.model.*; import com.pingplusplus.serializer.*; +import com.pingplusplus.util.GsonUtils; import java.io.*; import java.lang.reflect.InvocationTargetException; @@ -41,8 +41,7 @@ protected enum RequestMethod { /** * Gson object use to transform json string to resource object */ - public static final Gson GSON = new GsonBuilder() - .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) + public static final Gson GSON = GsonUtils.baseGsonBuilder() .registerTypeAdapter(Charge.class, new ChargeDeserializer()) .registerTypeAdapter(RedEnvelope.class, new RedEnvelopeDeserializer()) .registerTypeAdapter(Transfer.class, new TransferDeserializer()) @@ -106,18 +105,12 @@ protected static String className(Class clazz) { return "royalty_template"; case "balancesettlement": return "balance_settlement"; - case "subbank": - return "sub_bank"; case "splitreceiver": return "split_receiver"; case "splitprofit": return "split_profit"; case "profittransaction": return "profit_transaction"; - case "userpic": - return "users/upload_pic"; - case "contact": - return "sub_apps/contact"; default: return className; } diff --git a/src/main/java/com/pingplusplus/net/AppBasedResource.java b/pingpp-sdk/src/main/java/com/pingplusplus/net/AppBasedResource.java similarity index 100% rename from src/main/java/com/pingplusplus/net/AppBasedResource.java rename to pingpp-sdk/src/main/java/com/pingplusplus/net/AppBasedResource.java diff --git a/src/main/java/com/pingplusplus/net/HttpClient.java b/pingpp-sdk/src/main/java/com/pingplusplus/net/HttpClient.java similarity index 100% rename from src/main/java/com/pingplusplus/net/HttpClient.java rename to pingpp-sdk/src/main/java/com/pingplusplus/net/HttpClient.java diff --git a/src/main/java/com/pingplusplus/net/HttpContent.java b/pingpp-sdk/src/main/java/com/pingplusplus/net/HttpContent.java similarity index 100% rename from src/main/java/com/pingplusplus/net/HttpContent.java rename to pingpp-sdk/src/main/java/com/pingplusplus/net/HttpContent.java diff --git a/src/main/java/com/pingplusplus/net/HttpHeaders.java b/pingpp-sdk/src/main/java/com/pingplusplus/net/HttpHeaders.java similarity index 100% rename from src/main/java/com/pingplusplus/net/HttpHeaders.java rename to pingpp-sdk/src/main/java/com/pingplusplus/net/HttpHeaders.java diff --git a/src/main/java/com/pingplusplus/net/HttpURLConnectionClient.java b/pingpp-sdk/src/main/java/com/pingplusplus/net/HttpURLConnectionClient.java similarity index 87% rename from src/main/java/com/pingplusplus/net/HttpURLConnectionClient.java rename to pingpp-sdk/src/main/java/com/pingplusplus/net/HttpURLConnectionClient.java index af39110..41d2bcd 100644 --- a/src/main/java/com/pingplusplus/net/HttpURLConnectionClient.java +++ b/pingpp-sdk/src/main/java/com/pingplusplus/net/HttpURLConnectionClient.java @@ -2,6 +2,8 @@ import com.pingplusplus.Pingpp; import com.pingplusplus.exception.APIConnectionException; +import com.pingplusplus.exception.AuthenticationException; +import com.pingplusplus.util.PingppSignature; import com.pingplusplus.util.StreamUtils; import com.pingplusplus.util.StringUtils; @@ -37,6 +39,10 @@ public PingppResponse request(PingppRequest request) throws APIConnectionExcepti if (responseCode >= 200 && responseCode < 300) { responseBody = StreamUtils.readToEnd(conn.getInputStream(), APIResource.CHARSET); + boolean verified = PingppSignature.verify(headers, responseBody, request.options.getVerifyPublicKey(), APIResource.CHARSET.name()); + if (!verified) { + throw new AuthenticationException("响应签名验证失败,请检查验签公钥是否正确"); + } } else { responseBody = StreamUtils.readToEnd(conn.getErrorStream(), APIResource.CHARSET); } @@ -49,6 +55,8 @@ public PingppResponse request(PingppRequest request) throws APIConnectionExcepti + "Please check your internet connection and try again.", Pingpp.getApiBase(), e.getMessage()), e); + } catch (AuthenticationException e) { + throw new RuntimeException(e); } finally { if (conn != null) { conn.disconnect(); diff --git a/src/main/java/com/pingplusplus/net/PingppRequest.java b/pingpp-sdk/src/main/java/com/pingplusplus/net/PingppRequest.java similarity index 100% rename from src/main/java/com/pingplusplus/net/PingppRequest.java rename to pingpp-sdk/src/main/java/com/pingplusplus/net/PingppRequest.java diff --git a/src/main/java/com/pingplusplus/net/PingppResponse.java b/pingpp-sdk/src/main/java/com/pingplusplus/net/PingppResponse.java similarity index 100% rename from src/main/java/com/pingplusplus/net/PingppResponse.java rename to pingpp-sdk/src/main/java/com/pingplusplus/net/PingppResponse.java diff --git a/src/main/java/com/pingplusplus/net/RequestOptions.java b/pingpp-sdk/src/main/java/com/pingplusplus/net/RequestOptions.java similarity index 84% rename from src/main/java/com/pingplusplus/net/RequestOptions.java rename to pingpp-sdk/src/main/java/com/pingplusplus/net/RequestOptions.java index 4257b47..7a2b165 100644 --- a/src/main/java/com/pingplusplus/net/RequestOptions.java +++ b/pingpp-sdk/src/main/java/com/pingplusplus/net/RequestOptions.java @@ -6,6 +6,7 @@ public class RequestOptions { private final String apiKey; private final String appId; private final String privateKey; + private final String verifyPublicKey; private final int connectTimeout; private final int readTimeout; @@ -19,6 +20,7 @@ public static RequestOptions getDefault() { Pingpp.apiKey, Pingpp.appId, Pingpp.privateKey, + Pingpp.verifyPublicKey, Pingpp.getConnectTimeout(), Pingpp.getReadTimeout(), Pingpp.getMaxNetworkRetries(), @@ -29,6 +31,7 @@ private RequestOptions( String apiKey, String appId, String privateKey, + String verifyPublicKey, int connectTimeout, int readTimeout, int maxNetworkRetries, @@ -36,6 +39,7 @@ private RequestOptions( this.apiKey = apiKey; this.appId = appId; this.privateKey = privateKey; + this.verifyPublicKey = verifyPublicKey; this.connectTimeout = connectTimeout; this.readTimeout = readTimeout; this.maxNetworkRetries = maxNetworkRetries; @@ -70,6 +74,8 @@ public String getAcceptLanguage() { return acceptLanguage; } + public String getVerifyPublicKey() {return verifyPublicKey;} + public static RequestOptionsBuilder builder() { return new RequestOptionsBuilder(); } @@ -83,10 +89,22 @@ public static final class RequestOptionsBuilder { private int maxNetworkRetries; private String acceptLanguage; + public String getVerifyPublicKey() { + return verifyPublicKey; + } + + public RequestOptionsBuilder setVerifyPublicKey(String verifyPublicKey) { + this.verifyPublicKey = normalizePublicVerifyKey(verifyPublicKey); + return this; + } + + private String verifyPublicKey; + public RequestOptionsBuilder() { this.apiKey = Pingpp.apiKey; this.appId = Pingpp.appId; this.privateKey = Pingpp.privateKey; + this.verifyPublicKey = Pingpp.verifyPublicKey; this.connectTimeout = Pingpp.getConnectTimeout(); this.readTimeout = Pingpp.getReadTimeout(); this.maxNetworkRetries = Pingpp.getMaxNetworkRetries(); @@ -125,6 +143,11 @@ public RequestOptionsBuilder setPrivateKey(String privateKey) { return this; } + public RequestOptionsBuilder clearVerifyPublicKey() { + this.verifyPublicKey = null; + return this; + } + public RequestOptionsBuilder clearAppId() { this.appId = null; return this; @@ -171,6 +194,7 @@ public RequestOptions build() { normalizeApiKey(this.apiKey), normalizeAppId(this.appId), normalizePrivateKey(this.privateKey), + normalizePublicVerifyKey(this.verifyPublicKey), connectTimeout, readTimeout, maxNetworkRetries, @@ -214,6 +238,17 @@ private static String normalizePrivateKey(String privateKey) { return normalized; } + private static String normalizePublicVerifyKey(String verifyPublicKey) { + if (verifyPublicKey == null) { + return null; + } + String normalized = verifyPublicKey.trim(); + if (normalized.isEmpty()) { + throw new InvalidRequestOptionsException("Empty publicVerifyKey specified!"); + } + return normalized; + } + private static String normalizeAcceptLanguage(String acceptLanguage) { // null acceptLanguage are considered "valid" if (acceptLanguage == null) { diff --git a/src/main/java/com/pingplusplus/net/SubAppBasedResource.java b/pingpp-sdk/src/main/java/com/pingplusplus/net/SubAppBasedResource.java similarity index 100% rename from src/main/java/com/pingplusplus/net/SubAppBasedResource.java rename to pingpp-sdk/src/main/java/com/pingplusplus/net/SubAppBasedResource.java diff --git a/src/main/java/com/pingplusplus/net/UserBasedResource.java b/pingpp-sdk/src/main/java/com/pingplusplus/net/UserBasedResource.java similarity index 100% rename from src/main/java/com/pingplusplus/net/UserBasedResource.java rename to pingpp-sdk/src/main/java/com/pingplusplus/net/UserBasedResource.java diff --git a/src/main/java/com/pingplusplus/serializer/BatchRefundChargesSerializer.java b/pingpp-sdk/src/main/java/com/pingplusplus/serializer/BatchRefundChargesSerializer.java similarity index 100% rename from src/main/java/com/pingplusplus/serializer/BatchRefundChargesSerializer.java rename to pingpp-sdk/src/main/java/com/pingplusplus/serializer/BatchRefundChargesSerializer.java diff --git a/src/main/java/com/pingplusplus/serializer/BatchTransferRecipientSerializer.java b/pingpp-sdk/src/main/java/com/pingplusplus/serializer/BatchTransferRecipientSerializer.java similarity index 100% rename from src/main/java/com/pingplusplus/serializer/BatchTransferRecipientSerializer.java rename to pingpp-sdk/src/main/java/com/pingplusplus/serializer/BatchTransferRecipientSerializer.java diff --git a/src/main/java/com/pingplusplus/serializer/ChargeDeserializer.java b/pingpp-sdk/src/main/java/com/pingplusplus/serializer/ChargeDeserializer.java similarity index 78% rename from src/main/java/com/pingplusplus/serializer/ChargeDeserializer.java rename to pingpp-sdk/src/main/java/com/pingplusplus/serializer/ChargeDeserializer.java index 6043e2c..15ee2e3 100644 --- a/src/main/java/com/pingplusplus/serializer/ChargeDeserializer.java +++ b/pingpp-sdk/src/main/java/com/pingplusplus/serializer/ChargeDeserializer.java @@ -1,8 +1,6 @@ package com.pingplusplus.serializer; -import com.google.gson.FieldNamingPolicy; import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; @@ -11,6 +9,7 @@ import com.pingplusplus.model.App; import com.pingplusplus.model.Charge; import com.pingplusplus.model.ChargeRefundCollection; +import com.pingplusplus.util.GsonUtils; import java.lang.reflect.Type; @@ -20,40 +19,41 @@ public class ChargeDeserializer implements JsonDeserializer { @Override - public Charge deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { - + public Charge deserialize(JsonElement jsonElement, + Type type, + JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { JsonObject chargeJson = jsonElement.getAsJsonObject(); if (null != chargeJson.getAsJsonObject("credential")) { JsonObject credentialJson = chargeJson.getAsJsonObject("credential"); JsonObject channelCredential; if (credentialJson.getAsJsonObject("wx") != null) { JsonObject wx = credentialJson.getAsJsonObject("wx"); - Long timeStamp = wx.get("timeStamp").getAsLong(); + long timeStamp = wx.get("timeStamp").getAsLong(); wx.addProperty("timeStamp", Long.toString(timeStamp)); } else if (credentialJson.getAsJsonObject("wx_pub") != null) { JsonObject wxPub = credentialJson.getAsJsonObject("wx_pub"); if (null == wxPub.get("signed_data") && wxPub.get("timeStamp") != null) { - Long timeStamp = wxPub.get("timeStamp").getAsLong(); + long timeStamp = wxPub.get("timeStamp").getAsLong(); wxPub.addProperty("timeStamp", Long.toString(timeStamp)); } } else if ((channelCredential = credentialJson.getAsJsonObject("bfb")) != null || (channelCredential = credentialJson.getAsJsonObject("bfb_wap")) != null) { if (channelCredential.has("total_amount")) { - Long total_amount = channelCredential.get("total_amount").getAsLong(); + long total_amount = channelCredential.get("total_amount").getAsLong(); channelCredential.addProperty("total_amount", Long.toString(total_amount)); } } else if ((channelCredential = credentialJson.getAsJsonObject("alipay")) != null || (channelCredential = credentialJson.getAsJsonObject("alipay_wap")) != null || (channelCredential = credentialJson.getAsJsonObject("alipay_pc_direct")) != null) { if (channelCredential.has("payment_type")) { - Long paymentType = channelCredential.get("payment_type").getAsLong(); + long paymentType = channelCredential.get("payment_type").getAsLong(); channelCredential.addProperty("payment_type", Long.toString(paymentType)); } } } - Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES). - registerTypeAdapter(ChargeRefundCollection.class, new ChargeRefundCollectionDeserializer()) + Gson gson = GsonUtils.baseGsonBuilder() + .registerTypeAdapter(ChargeRefundCollection.class, new ChargeRefundCollectionDeserializer()) .create(); JsonElement appElement = chargeJson.get("app"); Charge charge = gson.fromJson(jsonElement, Charge.class); diff --git a/src/main/java/com/pingplusplus/serializer/ChargeEssentialsSerializer.java b/pingpp-sdk/src/main/java/com/pingplusplus/serializer/ChargeEssentialsSerializer.java similarity index 100% rename from src/main/java/com/pingplusplus/serializer/ChargeEssentialsSerializer.java rename to pingpp-sdk/src/main/java/com/pingplusplus/serializer/ChargeEssentialsSerializer.java diff --git a/src/main/java/com/pingplusplus/serializer/ChargeRefundCollectionDeserializer.java b/pingpp-sdk/src/main/java/com/pingplusplus/serializer/ChargeRefundCollectionDeserializer.java similarity index 65% rename from src/main/java/com/pingplusplus/serializer/ChargeRefundCollectionDeserializer.java rename to pingpp-sdk/src/main/java/com/pingplusplus/serializer/ChargeRefundCollectionDeserializer.java index 8b1231a..48616ac 100644 --- a/src/main/java/com/pingplusplus/serializer/ChargeRefundCollectionDeserializer.java +++ b/pingpp-sdk/src/main/java/com/pingplusplus/serializer/ChargeRefundCollectionDeserializer.java @@ -1,26 +1,20 @@ package com.pingplusplus.serializer; -import com.google.gson.FieldNamingPolicy; import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonParseException; -import com.google.gson.reflect.TypeToken; import com.pingplusplus.model.ChargeRefundCollection; -import com.pingplusplus.model.Refund; +import com.pingplusplus.util.GsonUtils; import java.lang.reflect.Type; -import java.util.List; public class ChargeRefundCollectionDeserializer implements JsonDeserializer { public ChargeRefundCollection deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { - Gson gson = new GsonBuilder() - .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) - .create(); + Gson gson = GsonUtils.baseGsonBuilder().create(); return gson.fromJson(json, typeOfT); } diff --git a/src/main/java/com/pingplusplus/serializer/CouponTemplateExpirationSerializer.java b/pingpp-sdk/src/main/java/com/pingplusplus/serializer/CouponTemplateExpirationSerializer.java similarity index 100% rename from src/main/java/com/pingplusplus/serializer/CouponTemplateExpirationSerializer.java rename to pingpp-sdk/src/main/java/com/pingplusplus/serializer/CouponTemplateExpirationSerializer.java diff --git a/src/main/java/com/pingplusplus/serializer/DoubleTypeSerializer.java b/pingpp-sdk/src/main/java/com/pingplusplus/serializer/DoubleTypeSerializer.java similarity index 100% rename from src/main/java/com/pingplusplus/serializer/DoubleTypeSerializer.java rename to pingpp-sdk/src/main/java/com/pingplusplus/serializer/DoubleTypeSerializer.java diff --git a/src/main/java/com/pingplusplus/serializer/EventDataDeserializer.java b/pingpp-sdk/src/main/java/com/pingplusplus/serializer/EventDataDeserializer.java similarity index 100% rename from src/main/java/com/pingplusplus/serializer/EventDataDeserializer.java rename to pingpp-sdk/src/main/java/com/pingplusplus/serializer/EventDataDeserializer.java diff --git a/pingpp-sdk/src/main/java/com/pingplusplus/serializer/ObjectListDeserializer.java b/pingpp-sdk/src/main/java/com/pingplusplus/serializer/ObjectListDeserializer.java new file mode 100644 index 0000000..134ba36 --- /dev/null +++ b/pingpp-sdk/src/main/java/com/pingplusplus/serializer/ObjectListDeserializer.java @@ -0,0 +1,49 @@ +package com.pingplusplus.serializer; + +import com.google.gson.*; +import com.pingplusplus.util.GsonUtils; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; + +public class ObjectListDeserializer implements JsonDeserializer> { + + @Override + public List deserialize(JsonElement elem, + Type type, + JsonDeserializationContext context) { + List list = new ArrayList<>(); + JsonArray jsonArray = elem.getAsJsonArray(); + + for (JsonElement element : jsonArray) { + if (element.isJsonNull()) { + list.add(null); + } else if (element.isJsonPrimitive()) { + JsonPrimitive primitive = element.getAsJsonPrimitive(); + if (primitive.isNumber()) { + String numStr = primitive.getAsString(); + if (numStr.contains(".")) { + list.add(primitive.getAsDouble()); + } else { + try { + list.add(primitive.getAsLong()); + } catch (NumberFormatException e) { + list.add(primitive.getAsBigDecimal()); + } + } + } else if (primitive.isString()) { + list.add(primitive.getAsString()); + } else if (primitive.isBoolean()) { + list.add(primitive.getAsBoolean()); + } + } else if (element.isJsonArray()) { + list.add(context.deserialize(element, GsonUtils.LIST_OBJ)); + } else if (element.isJsonObject()) { + list.add(context.deserialize(element, GsonUtils.MAP_STR_OBJ)); + } + } + + return list; + } +} \ No newline at end of file diff --git a/src/main/java/com/pingplusplus/serializer/PingppRawJsonObjectDeserializer.java b/pingpp-sdk/src/main/java/com/pingplusplus/serializer/PingppRawJsonObjectDeserializer.java similarity index 100% rename from src/main/java/com/pingplusplus/serializer/PingppRawJsonObjectDeserializer.java rename to pingpp-sdk/src/main/java/com/pingplusplus/serializer/PingppRawJsonObjectDeserializer.java diff --git a/src/main/java/com/pingplusplus/serializer/RedEnvelopeDeserializer.java b/pingpp-sdk/src/main/java/com/pingplusplus/serializer/RedEnvelopeDeserializer.java similarity index 79% rename from src/main/java/com/pingplusplus/serializer/RedEnvelopeDeserializer.java rename to pingpp-sdk/src/main/java/com/pingplusplus/serializer/RedEnvelopeDeserializer.java index e2b6ee9..251279b 100644 --- a/src/main/java/com/pingplusplus/serializer/RedEnvelopeDeserializer.java +++ b/pingpp-sdk/src/main/java/com/pingplusplus/serializer/RedEnvelopeDeserializer.java @@ -1,8 +1,6 @@ package com.pingplusplus.serializer; -import com.google.gson.FieldNamingPolicy; import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; @@ -11,6 +9,7 @@ import com.pingplusplus.model.App; import com.pingplusplus.model.ChargeRefundCollection; import com.pingplusplus.model.RedEnvelope; +import com.pingplusplus.util.GsonUtils; import java.lang.reflect.Type; @@ -21,8 +20,8 @@ public class RedEnvelopeDeserializer implements JsonDeserializer { @Override public RedEnvelope deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { JsonObject transFerJson = json.getAsJsonObject(); - Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES). - registerTypeAdapter(ChargeRefundCollection.class, new ChargeRefundCollectionDeserializer()) + Gson gson = GsonUtils.baseGsonBuilder() + .registerTypeAdapter(ChargeRefundCollection.class, new ChargeRefundCollectionDeserializer()) .create(); JsonElement appElement = transFerJson.get("app"); RedEnvelope redEnvelope = gson.fromJson(json, RedEnvelope.class); diff --git a/src/main/java/com/pingplusplus/serializer/SettleAccountRecipientSerializer.java b/pingpp-sdk/src/main/java/com/pingplusplus/serializer/SettleAccountRecipientSerializer.java similarity index 100% rename from src/main/java/com/pingplusplus/serializer/SettleAccountRecipientSerializer.java rename to pingpp-sdk/src/main/java/com/pingplusplus/serializer/SettleAccountRecipientSerializer.java diff --git a/pingpp-sdk/src/main/java/com/pingplusplus/serializer/StringObjectMapDeserializer.java b/pingpp-sdk/src/main/java/com/pingplusplus/serializer/StringObjectMapDeserializer.java new file mode 100644 index 0000000..42ad230 --- /dev/null +++ b/pingpp-sdk/src/main/java/com/pingplusplus/serializer/StringObjectMapDeserializer.java @@ -0,0 +1,52 @@ +package com.pingplusplus.serializer; + +import com.google.gson.*; +import com.pingplusplus.util.GsonUtils; + +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.Map; + +public class StringObjectMapDeserializer implements JsonDeserializer> { + + @Override + public Map deserialize(JsonElement elem, + Type type, + JsonDeserializationContext context) { + Map map = new HashMap<>(); + JsonObject jsonObject = elem.getAsJsonObject(); + + for (Map.Entry entry : jsonObject.entrySet()) { + String key = entry.getKey(); + JsonElement value = entry.getValue(); + + if (value.isJsonNull()) { + map.put(key, null); + } else if (value.isJsonPrimitive()) { + JsonPrimitive primitive = value.getAsJsonPrimitive(); + if (primitive.isNumber()) { + String numStr = primitive.getAsString(); + if (numStr.contains(".")) { + map.put(key, primitive.getAsDouble()); + } else { + try { + map.put(key, primitive.getAsLong()); + } catch (NumberFormatException e) { + map.put(key, primitive.getAsBigDecimal()); + } + } + } else if (primitive.isString()) { + map.put(key, primitive.getAsString()); + } else if (primitive.isBoolean()) { + map.put(key, primitive.getAsBoolean()); + } + } else if (value.isJsonArray()) { + map.put(key, context.deserialize(value, GsonUtils.LIST_OBJ)); + } else if (value.isJsonObject()) { + map.put(key, context.deserialize(value, GsonUtils.MAP_STR_OBJ)); + } + } + + return map; + } +} \ No newline at end of file diff --git a/src/main/java/com/pingplusplus/serializer/SubAppDeserializer.java b/pingpp-sdk/src/main/java/com/pingplusplus/serializer/SubAppDeserializer.java similarity index 84% rename from src/main/java/com/pingplusplus/serializer/SubAppDeserializer.java rename to pingpp-sdk/src/main/java/com/pingplusplus/serializer/SubAppDeserializer.java index e3aa75a..a137464 100644 --- a/src/main/java/com/pingplusplus/serializer/SubAppDeserializer.java +++ b/pingpp-sdk/src/main/java/com/pingplusplus/serializer/SubAppDeserializer.java @@ -2,6 +2,7 @@ import com.google.gson.*; import com.pingplusplus.model.*; +import com.pingplusplus.util.GsonUtils; import java.lang.reflect.Type; @@ -16,9 +17,7 @@ public SubApp deserialize(JsonElement jsonElement, Type type, JsonDeserializatio JsonObject subAppJson = jsonElement.getAsJsonObject(); - Gson gson = new GsonBuilder() - .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) - .create(); + Gson gson = GsonUtils.baseGsonBuilder().create(); JsonElement userElement = subAppJson.get("user"); SubApp subApp = gson.fromJson(jsonElement, SubApp.class); diff --git a/src/main/java/com/pingplusplus/serializer/TransferDeserializer.java b/pingpp-sdk/src/main/java/com/pingplusplus/serializer/TransferDeserializer.java similarity index 86% rename from src/main/java/com/pingplusplus/serializer/TransferDeserializer.java rename to pingpp-sdk/src/main/java/com/pingplusplus/serializer/TransferDeserializer.java index cc7812f..c0ed8d9 100644 --- a/src/main/java/com/pingplusplus/serializer/TransferDeserializer.java +++ b/pingpp-sdk/src/main/java/com/pingplusplus/serializer/TransferDeserializer.java @@ -1,8 +1,6 @@ package com.pingplusplus.serializer; -import com.google.gson.FieldNamingPolicy; import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; @@ -11,6 +9,7 @@ import com.pingplusplus.model.App; import com.pingplusplus.model.ChargeRefundCollection; import com.pingplusplus.model.Transfer; +import com.pingplusplus.util.GsonUtils; import java.lang.reflect.Type; @@ -22,7 +21,7 @@ public class TransferDeserializer implements JsonDeserializer { public Transfer deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { JsonObject transFerJson = json.getAsJsonObject(); - Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) + Gson gson = GsonUtils.baseGsonBuilder() .registerTypeAdapter(ChargeRefundCollection.class, new ChargeRefundCollectionDeserializer()) .create(); JsonElement appElement = transFerJson.get("app"); diff --git a/src/main/java/com/pingplusplus/util/CaseInsensitiveMap.java b/pingpp-sdk/src/main/java/com/pingplusplus/util/CaseInsensitiveMap.java similarity index 90% rename from src/main/java/com/pingplusplus/util/CaseInsensitiveMap.java rename to pingpp-sdk/src/main/java/com/pingplusplus/util/CaseInsensitiveMap.java index aadfaa0..15f13cb 100644 --- a/src/main/java/com/pingplusplus/util/CaseInsensitiveMap.java +++ b/pingpp-sdk/src/main/java/com/pingplusplus/util/CaseInsensitiveMap.java @@ -82,17 +82,17 @@ public void clear() { @Override public Set keySet() { - return this.store.values().stream().map(entry -> entry.getKey()).collect(Collectors.toSet()); + return this.store.values().stream().map(Entry::getKey).collect(Collectors.toSet()); } @Override public Collection values() { - return this.store.values().stream().map(entry -> entry.getValue()).collect(Collectors.toList()); + return this.store.values().stream().map(Entry::getValue).collect(Collectors.toList()); } @Override public Set> entrySet() { - return this.store.values().stream().collect(Collectors.toSet()); + return new HashSet<>(this.store.values()); } // Utility diff --git a/pingpp-sdk/src/main/java/com/pingplusplus/util/GsonUtils.java b/pingpp-sdk/src/main/java/com/pingplusplus/util/GsonUtils.java new file mode 100644 index 0000000..6187708 --- /dev/null +++ b/pingpp-sdk/src/main/java/com/pingplusplus/util/GsonUtils.java @@ -0,0 +1,25 @@ +package com.pingplusplus.util; + +import java.lang.reflect.Type; +import java.util.List; +import java.util.Map; + +import com.google.gson.FieldNamingPolicy; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; +import com.pingplusplus.serializer.ObjectListDeserializer; +import com.pingplusplus.serializer.StringObjectMapDeserializer; + +public final class GsonUtils { + public static final Type MAP_STR_OBJ = new TypeToken>() {}.getType(); + public static final Type LIST_OBJ = new TypeToken>() {}.getType(); + + private GsonUtils() {} + + public static GsonBuilder baseGsonBuilder() { + return new GsonBuilder() + .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) + .registerTypeAdapter(GsonUtils.MAP_STR_OBJ, new StringObjectMapDeserializer()) + .registerTypeAdapter(GsonUtils.LIST_OBJ, new ObjectListDeserializer()); + } +} diff --git a/pingpp-sdk/src/main/java/com/pingplusplus/util/PingppSignature.java b/pingpp-sdk/src/main/java/com/pingplusplus/util/PingppSignature.java new file mode 100644 index 0000000..a1db312 --- /dev/null +++ b/pingpp-sdk/src/main/java/com/pingplusplus/util/PingppSignature.java @@ -0,0 +1,150 @@ +package com.pingplusplus.util; + +import com.pingplusplus.Pingpp; +import com.pingplusplus.exception.AuthenticationException; +import com.pingplusplus.net.HttpHeaders; +import org.apache.commons.codec.binary.Base64; + +import java.io.UnsupportedEncodingException; +import java.security.*; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +/** + * Created by Afon on 2016/12/16. + */ +public class PingppSignature { + public static String sign(String data, String PEMEncodedPrivateKey, String charset) { + PrivateKey privateKey = getPrivateKeyFromPEM(PEMEncodedPrivateKey); + if (privateKey == null) { + return null; + } + + try { + Signature signature = Signature.getInstance("SHA256withRSA"); + signature.initSign(privateKey); + signature.update(data.getBytes(charset)); + byte[] signBytes = signature.sign(); + + return Base64.encodeBase64String(signBytes).replaceAll("[\n\r]", ""); + } catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException | UnsupportedEncodingException e) { + e.printStackTrace(); + } + + return null; + } + + /** + * webhook verify + * @param encodedSign + * @param data + * @param PEMEncodedPublicKey + * @param charset + * @return + * @throws AuthenticationException + */ + public static boolean verify(String encodedSign, String data, String PEMEncodedPublicKey, String charset) throws AuthenticationException { + if (StringUtils.isBlank(PEMEncodedPublicKey)) { + return true; + } + PublicKey publicKey = getPublicKeyFromPEM(PEMEncodedPublicKey); + if (publicKey == null) { + return true; + } + if (StringUtils.isBlank(encodedSign)) { + throw new AuthenticationException("响应签名 (X-Pingplusplus-Signature) 为空"); + } + try { + Signature signature = Signature.getInstance("SHA256withRSA"); + signature.initVerify(publicKey); + signature.update(data.getBytes(charset)); + return signature.verify(Base64.decodeBase64(encodedSign)); + } catch (InvalidKeyException | UnsupportedEncodingException | SignatureException | NoSuchAlgorithmException e) { + if (Pingpp.DEBUG) { + e.printStackTrace(); + } + return false; + } + } + + public static boolean verify(HttpHeaders headers, String data, String PEMEncodedPublicKey, String charset) throws AuthenticationException { + if (StringUtils.isBlank(PEMEncodedPublicKey)) { + return true; + } + PublicKey publicKey = getPublicKeyFromPEM(PEMEncodedPublicKey); + if (publicKey == null) { + return true; + } + if (!headers.firstValue("X-Pingplusplus-Signature").isPresent()) { + throw new AuthenticationException("响应签名 (X-Pingplusplus-Signature) 为空"); + } + String encodedSign = headers.firstValue("X-Pingplusplus-Signature").get(); + try { + Signature signature = Signature.getInstance("SHA256withRSA"); + signature.initVerify(publicKey); + signature.update(data.getBytes(charset)); + return signature.verify(Base64.decodeBase64(encodedSign)); + } catch (InvalidKeyException | UnsupportedEncodingException | SignatureException | NoSuchAlgorithmException e) { + if (Pingpp.DEBUG) { + e.printStackTrace(); + } + return false; + } + } + + + public static PrivateKey getPrivateKeyFromPEM(String PEMEncodedPrivateKey) { + PEMEncodedPrivateKey = PEMEncodedPrivateKey + .replaceAll("(-+BEGIN (RSA )?PRIVATE KEY-+\\r?\\n|-+END (RSA )?PRIVATE KEY-+\\r?\\n?)", ""); + byte[] privateKeyBytes = Base64.decodeBase64(PEMEncodedPrivateKey); + + try { + return generatePrivateKeyWithPKCS8(privateKeyBytes); + } catch (InvalidKeySpecException e) { + if (Pingpp.DEBUG) { + e.printStackTrace(); + } + return null; + } + } + + public static PrivateKey generatePrivateKeyWithPKCS8(byte[] privateKeyBytes) + throws InvalidKeySpecException { + try { + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes); + KeyFactory kf = KeyFactory.getInstance("RSA"); + return kf.generatePrivate(keySpec); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + return null; + } + + public static PublicKey getPublicKeyFromPEM(String PEMEncodedPublicKey) { + PEMEncodedPublicKey = PEMEncodedPublicKey + .replaceAll("(-+BEGIN (RSA )?PUBLIC KEY-+\\r?\\n|-+END (RSA )?PUBLIC KEY-+\\r?\\n?)", ""); + byte[] publicKeyBytes = Base64.decodeBase64(PEMEncodedPublicKey); + + try { + return generatePublicKey(publicKeyBytes); + } catch (InvalidKeySpecException e) { + if (Pingpp.DEBUG) { + e.printStackTrace(); + } + return null; + } + } + + public static PublicKey generatePublicKey(byte[] publicKeyBytes) + throws InvalidKeySpecException { + try { + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes); + KeyFactory kf = KeyFactory.getInstance("RSA"); + return kf.generatePublic(keySpec); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/src/main/java/com/pingplusplus/util/StreamUtils.java b/pingpp-sdk/src/main/java/com/pingplusplus/util/StreamUtils.java similarity index 100% rename from src/main/java/com/pingplusplus/util/StreamUtils.java rename to pingpp-sdk/src/main/java/com/pingplusplus/util/StreamUtils.java diff --git a/src/main/java/com/pingplusplus/util/StringUtils.java b/pingpp-sdk/src/main/java/com/pingplusplus/util/StringUtils.java similarity index 68% rename from src/main/java/com/pingplusplus/util/StringUtils.java rename to pingpp-sdk/src/main/java/com/pingplusplus/util/StringUtils.java index 790145d..ef6ce39 100644 --- a/src/main/java/com/pingplusplus/util/StringUtils.java +++ b/pingpp-sdk/src/main/java/com/pingplusplus/util/StringUtils.java @@ -35,4 +35,25 @@ public static String join(String separator, List input) { return sb.toString(); } + + /** + * 是否为空 + * + * @param cs 字符串 + * @return 空 {@code true}; 非空 {@code false}. + */ + public static boolean isBlank(final CharSequence cs) { + if (cs == null) { + return true; + } + int l = cs.length(); + if (l > 0) { + for (int i = 0; i < l; i++) { + if (!Character.isWhitespace(cs.charAt(i))) { + return false; + } + } + } + return true; + } } diff --git a/src/main/java/com/pingplusplus/util/WxLiteOAuth.java b/pingpp-sdk/src/main/java/com/pingplusplus/util/WxLiteOAuth.java similarity index 100% rename from src/main/java/com/pingplusplus/util/WxLiteOAuth.java rename to pingpp-sdk/src/main/java/com/pingplusplus/util/WxLiteOAuth.java diff --git a/src/main/java/com/pingplusplus/util/WxpubOAuth.java b/pingpp-sdk/src/main/java/com/pingplusplus/util/WxpubOAuth.java similarity index 95% rename from src/main/java/com/pingplusplus/util/WxpubOAuth.java rename to pingpp-sdk/src/main/java/com/pingplusplus/util/WxpubOAuth.java index d4aeced..d6a7815 100644 --- a/src/main/java/com/pingplusplus/util/WxpubOAuth.java +++ b/pingpp-sdk/src/main/java/com/pingplusplus/util/WxpubOAuth.java @@ -1,10 +1,12 @@ package com.pingplusplus.util; -import com.google.gson.*; +import com.google.gson.FieldNamingPolicy; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import com.pingplusplus.exception.ChannelException; import java.io.BufferedReader; -import java.io.IOException; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; @@ -157,8 +159,7 @@ public static String getJsapiTicket(String appId, String appSecret) throws Unsup String queryString = WxpubOAuth.httpBuildQuery(data); String accessTokenUrl = "https://api.weixin.qq.com/cgi-bin/token?" + queryString; String resp = httpGet(accessTokenUrl); - JsonParser jp = new JsonParser(); - JsonObject respJson = jp.parse(resp).getAsJsonObject(); + JsonObject respJson = JsonParser.parseString(resp).getAsJsonObject(); if (respJson.has("errcode")) { return respJson.toString(); } @@ -169,7 +170,7 @@ public static String getJsapiTicket(String appId, String appSecret) throws Unsup queryString = WxpubOAuth.httpBuildQuery(data); String jsapiTicketUrl = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?" + queryString; resp = httpGet(jsapiTicketUrl); - JsonObject ticket = jp.parse(resp).getAsJsonObject(); + JsonObject ticket = JsonParser.parseString(resp).getAsJsonObject(); return ticket.get("ticket").getAsString(); } @@ -183,8 +184,7 @@ public static String getJsapiTicket(String appId, String appSecret) throws Unsup public static String getSignature(String charge, String jsapiTicket, String url) { if (null == charge || null == jsapiTicket || charge.isEmpty() || jsapiTicket.isEmpty()) return null; - JsonParser jp = new JsonParser(); - JsonObject chargeJson = jp.parse(charge).getAsJsonObject(); + JsonObject chargeJson = JsonParser.parseString(charge).getAsJsonObject(); if (!chargeJson.has("credential")) { return null; } @@ -206,9 +206,7 @@ public static String getSignature(String charge, String jsapiTicket, String url) crypt.reset(); crypt.update(string1.getBytes(CHARSET)); signature = byteToHex(crypt.digest()); - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - } catch (UnsupportedEncodingException e) { + } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) { e.printStackTrace(); } diff --git a/src/test/java/com/pingplusplus/CardInfoTest.java b/pingpp-sdk/src/test/java/com/pingplusplus/CardInfoTest.java similarity index 100% rename from src/test/java/com/pingplusplus/CardInfoTest.java rename to pingpp-sdk/src/test/java/com/pingplusplus/CardInfoTest.java diff --git a/src/test/java/com/pingplusplus/PingppTest.java b/pingpp-sdk/src/test/java/com/pingplusplus/PingppTest.java similarity index 82% rename from src/test/java/com/pingplusplus/PingppTest.java rename to pingpp-sdk/src/test/java/com/pingplusplus/PingppTest.java index a66d413..c5b7676 100644 --- a/src/test/java/com/pingplusplus/PingppTest.java +++ b/pingpp-sdk/src/test/java/com/pingplusplus/PingppTest.java @@ -2,6 +2,7 @@ import com.pingplusplus.exception.PingppException; import com.pingplusplus.model.*; +import com.pingplusplus.net.APIResource; import org.junit.BeforeClass; import org.junit.Test; @@ -25,12 +26,55 @@ public class PingppTest { Pingpp.DEBUG = true; } + @Test public void testDeserialize() { + String jsonStr = "{\n" + + " \"id\": \"ch_1234567890\",\n" + + " \"object\": \"charge\",\n" + + " \"livemode\": true,\n" + + " \"paid\": true,\n" + + " \"refunded\": true,\n" + + " \"extra\": {\n" + + " \"discount_code\": \"ABCD\",\n" + + " \"discount_amount\": 20,\n" + + " \"score\": 60.12\n" + + " },\n" + + " \"time_paid\": 1732609210,\n" + + " \"time_expire\": 1732610502,\n" + + " \"time_settle\": null,\n" + + " \"transaction_no\": \"6523236536624\",\n" + + " \"amount_refunded\": 0,\n" + + " \"failure_code\": null,\n" + + " \"failure_msg\": null,\n" + + " \"metadata\": {\n" + + " \"code\": \"10000\",\n" + + " \"type\": \"PAYMENT\",\n" + + " \"list\": [10, 100, 1000]" + + " },\n" + + " \"credential\": {},\n" + + " \"description\": \"DESC\"\n" + + "}"; + Charge ch = APIResource.getGson().fromJson(jsonStr, Charge.class); + Object discountAmount = ch.getExtra().get("discount_amount"); + assertEquals(Long.class, discountAmount.getClass()); + assertEquals(20L, discountAmount); + Object score = ch.getExtra().get("score"); + assertEquals(Double.class, score.getClass()); + System.out.println("score value: " + score); + + Object metaCode = ch.getMetadata().get("code"); + assertEquals(String.class, metaCode.getClass()); + Object metaList = ch.getMetadata().get("list"); + assertEquals(ArrayList.class, metaList.getClass()); + Object metaListEle = ((ArrayList) metaList).get(0); + assertEquals(Long.class, metaListEle.getClass()); + } + @Test public void testSetApiKey() { assertEquals("apiKey should be set", "sk_test_ibbTe5jLGCi5rzfH4OqPW9KC", Pingpp.apiKey); } @Test public void testVerifyVersions() { - assertEquals("Pingpp.VERSION should match", "2.4.0", Pingpp.VERSION); + assertEquals("Pingpp.VERSION should match", "2.5.7", Pingpp.VERSION); } @Test public void testCreateCharge() { diff --git a/src/test/java/com/pingplusplus/PingppTestBase.java b/pingpp-sdk/src/test/java/com/pingplusplus/PingppTestBase.java similarity index 100% rename from src/test/java/com/pingplusplus/PingppTestBase.java rename to pingpp-sdk/src/test/java/com/pingplusplus/PingppTestBase.java diff --git a/src/test/java/com/pingplusplus/PingppTestData.java b/pingpp-sdk/src/test/java/com/pingplusplus/PingppTestData.java similarity index 77% rename from src/test/java/com/pingplusplus/PingppTestData.java rename to pingpp-sdk/src/test/java/com/pingplusplus/PingppTestData.java index a701fe3..815aa81 100644 --- a/src/test/java/com/pingplusplus/PingppTestData.java +++ b/pingpp-sdk/src/test/java/com/pingplusplus/PingppTestData.java @@ -18,34 +18,7 @@ public static String getAppID() { } public static String getPKCS8PrivateKey() { - return "-----BEGIN PRIVATE KEY-----\n" + - "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDHYyS3FwoESp1h\n" + - "GLYiBhy6k9Ag3lzGCIEvm50IIEkE0Ftc9qq44TWqyl+EHUpTMdcBOcI42JLO5stw\n" + - "FOfCLa3PQStEJ4llIRFEKlsrHh67pvWd5RNaSBrvGlnFY40S+SZmjk2WF/h9dE9R\n" + - "ic79t0YI0alD8dIl9Yu3OaEKo7VonBWFwOYMxjPhtORlq+EUF1XJd//yftQrKWTT\n" + - "d7KaUonWzBCl4VzFop/OyTWYlTuZz3eYJaNpH5VaQ1vDgBAcPIeBvMf7NgBHMKW6\n" + - "LLmFd2LEYQ/6I7hkGTjysSzWEpO8bPWT6OEsJ2R2kFGOrSkr+G2MDcJ7ykXYAmz5\n" + - "+A3plS6ZAgMBAAECggEAVrgwR9GlcahiOtDcpn+yDxQq+aC9CQS561LrQZWJLKbS\n" + - "leRS7IZHKTlLwdJbeUO8F7RfXQoVEBghc2YkRrhHWFUn1ES95VY0hElHzcET7Nn5\n" + - "CeuQNzwVOtljIg7iVNY4dXJ/HEDguu/Tb8tYU9FajItj60FJ/WiGk/JksJPzWsOC\n" + - "VPVniy9fTbTLy1e+dCpCI6OXirtm7hvbodRNDjree0wSEzm7vL0wVzEZFo6kX+AB\n" + - "GUwaoO7pPyH+hgyI5Iuhc65NHsHzTJpf8yNFl9QGhkxvm2Ff2oEtDt1idOTBrHB6\n" + - "tg+ti9Ctb2+2yzBnk14hsSYJnKitR7wM6ZCFPX4eYQKBgQD+JAREeFkodec/SC+G\n" + - "X+4Q4Y68uMPkfUPrMKXM4cyY5wgXk64RBvRVxIxX7x6Y3tIKn9v8tWAprbsyVr15\n" + - "eb4RcAFEVwjuoZixhd9sIPsRhfdNolKn/fSPIsHL4ywcJMSIt7KVKHuQeqBNHy0o\n" + - "0PxQjNej1ozsmrAWqV55cbKHswKBgQDI2JQRTPIEC/2y6LdmBVhGJW9OKWTYdVNj\n" + - "q7rX+Yw4uxOtfd5hBqpvgZEklKEk72aazFdEcERlAm9SqoX09qk6zK/wcq4Xn5Q/\n" + - "qy8ecmjuyf2AK9X+HUdMerMVxhK9RpeevKYP/RO2F/wIN64anlQVYygVkXXgdOvW\n" + - "hBE4YABKgwKBgDRtmbPGYB5ItHwJmERQZfx1i8zDESaB8RED6DBsJJkmkDTM8ovw\n" + - "s1c+RPWfDuDalto6QFfR0xTGEmhAHLaCtwNB6AEBM4aHL8jvpTfZVfI3gN0zL3oY\n" + - "mestcG1vYBouO504yE6dG2Ci6479b4OMGYFEjPfvuwLUpp8GMcc7/WihAoGANCp8\n" + - "mtm/ammq5VMof2kX+nAyrrx1ovsmQ5cRGpOIZhvBCqjMn6rZjci7aCLqj+tWXRKC\n" + - "ABagzROK0o/T50JBxjHv6KYArcYW/Up7HI9ezdbM7wNzu2LjZ+veo+MkbuDs9J/P\n" + - "CgwTmJI2NfQwVl2VPVDZ0nBLi5cSwk7fIiNdL/0CgYEAtECmC1QDs53Di2MIsa/F\n" + - "e4sWfJGSDqEWqhcA/aPwf1skM6VJJXBBMV1qFtwgO1AlLnu9dQYra6ylsUoubVYI\n" + - "XM9XK7EMhbqi57+Q75jHFTc0DnzOTyho5Gp4Ddi8dztmZGNWdWTGdeMqh+svqMXk\n" + - "D6VdJeddyGu/Zlgj7Wk6whU=\n" + - "-----END PRIVATE KEY-----"; + return "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDHYyS3FwoESp1hGLYiBhy6k9Ag3lzGCIEvm50IIEkE0Ftc9qq44TWqyl+EHUpTMdcBOcI42JLO5stwFOfCLa3PQStEJ4llIRFEKlsrHh67pvWd5RNaSBrvGlnFY40S+SZmjk2WF/h9dE9Ric79t0YI0alD8dIl9Yu3OaEKo7VonBWFwOYMxjPhtORlq+EUF1XJd//yftQrKWTTd7KaUonWzBCl4VzFop/OyTWYlTuZz3eYJaNpH5VaQ1vDgBAcPIeBvMf7NgBHMKW6LLmFd2LEYQ/6I7hkGTjysSzWEpO8bPWT6OEsJ2R2kFGOrSkr+G2MDcJ7ykXYAmz5+A3plS6ZAgMBAAECggEAVrgwR9GlcahiOtDcpn+yDxQq+aC9CQS561LrQZWJLKbSleRS7IZHKTlLwdJbeUO8F7RfXQoVEBghc2YkRrhHWFUn1ES95VY0hElHzcET7Nn5CeuQNzwVOtljIg7iVNY4dXJ/HEDguu/Tb8tYU9FajItj60FJ/WiGk/JksJPzWsOCVPVniy9fTbTLy1e+dCpCI6OXirtm7hvbodRNDjree0wSEzm7vL0wVzEZFo6kX+ABGUwaoO7pPyH+hgyI5Iuhc65NHsHzTJpf8yNFl9QGhkxvm2Ff2oEtDt1idOTBrHB6tg+ti9Ctb2+2yzBnk14hsSYJnKitR7wM6ZCFPX4eYQKBgQD+JAREeFkodec/SC+GX+4Q4Y68uMPkfUPrMKXM4cyY5wgXk64RBvRVxIxX7x6Y3tIKn9v8tWAprbsyVr15eb4RcAFEVwjuoZixhd9sIPsRhfdNolKn/fSPIsHL4ywcJMSIt7KVKHuQeqBNHy0o0PxQjNej1ozsmrAWqV55cbKHswKBgQDI2JQRTPIEC/2y6LdmBVhGJW9OKWTYdVNjq7rX+Yw4uxOtfd5hBqpvgZEklKEk72aazFdEcERlAm9SqoX09qk6zK/wcq4Xn5Q/qy8ecmjuyf2AK9X+HUdMerMVxhK9RpeevKYP/RO2F/wIN64anlQVYygVkXXgdOvWhBE4YABKgwKBgDRtmbPGYB5ItHwJmERQZfx1i8zDESaB8RED6DBsJJkmkDTM8ovws1c+RPWfDuDalto6QFfR0xTGEmhAHLaCtwNB6AEBM4aHL8jvpTfZVfI3gN0zL3oYmestcG1vYBouO504yE6dG2Ci6479b4OMGYFEjPfvuwLUpp8GMcc7/WihAoGANCp8mtm/ammq5VMof2kX+nAyrrx1ovsmQ5cRGpOIZhvBCqjMn6rZjci7aCLqj+tWXRKCABagzROK0o/T50JBxjHv6KYArcYW/Up7HI9ezdbM7wNzu2LjZ+veo+MkbuDs9J/PCgwTmJI2NfQwVl2VPVDZ0nBLi5cSwk7fIiNdL/0CgYEAtECmC1QDs53Di2MIsa/Fe4sWfJGSDqEWqhcA/aPwf1skM6VJJXBBMV1qFtwgO1AlLnu9dQYra6ylsUoubVYIXM9XK7EMhbqi57+Q75jHFTc0DnzOTyho5Gp4Ddi8dztmZGNWdWTGdeMqh+svqMXkD6VdJeddyGu/Zlgj7Wk6whU="; } public static String getChargeWebhooksData() { diff --git a/src/test/java/com/pingplusplus/RequestOptionsTest.java b/pingpp-sdk/src/test/java/com/pingplusplus/RequestOptionsTest.java similarity index 100% rename from src/test/java/com/pingplusplus/RequestOptionsTest.java rename to pingpp-sdk/src/test/java/com/pingplusplus/RequestOptionsTest.java diff --git a/src/test/java/com/pingplusplus/WebhookTest.java b/pingpp-sdk/src/test/java/com/pingplusplus/WebhookTest.java similarity index 100% rename from src/test/java/com/pingplusplus/WebhookTest.java rename to pingpp-sdk/src/test/java/com/pingplusplus/WebhookTest.java diff --git a/src/test/java/com/pingplusplus/WeixinOpenidTest.java b/pingpp-sdk/src/test/java/com/pingplusplus/WeixinOpenidTest.java similarity index 100% rename from src/test/java/com/pingplusplus/WeixinOpenidTest.java rename to pingpp-sdk/src/test/java/com/pingplusplus/WeixinOpenidTest.java diff --git a/src/test/java/com/pingplusplus/agreement/AgreenmentTest.java b/pingpp-sdk/src/test/java/com/pingplusplus/agreement/AgreenmentTest.java similarity index 100% rename from src/test/java/com/pingplusplus/agreement/AgreenmentTest.java rename to pingpp-sdk/src/test/java/com/pingplusplus/agreement/AgreenmentTest.java diff --git a/src/test/java/com/pingplusplus/balance/BalanceBonusTest.java b/pingpp-sdk/src/test/java/com/pingplusplus/balance/BalanceBonusTest.java similarity index 100% rename from src/test/java/com/pingplusplus/balance/BalanceBonusTest.java rename to pingpp-sdk/src/test/java/com/pingplusplus/balance/BalanceBonusTest.java diff --git a/src/test/java/com/pingplusplus/balance/BalanceSettlementTest.java b/pingpp-sdk/src/test/java/com/pingplusplus/balance/BalanceSettlementTest.java similarity index 100% rename from src/test/java/com/pingplusplus/balance/BalanceSettlementTest.java rename to pingpp-sdk/src/test/java/com/pingplusplus/balance/BalanceSettlementTest.java diff --git a/src/test/java/com/pingplusplus/balance/BalanceTransactionTest.java b/pingpp-sdk/src/test/java/com/pingplusplus/balance/BalanceTransactionTest.java similarity index 100% rename from src/test/java/com/pingplusplus/balance/BalanceTransactionTest.java rename to pingpp-sdk/src/test/java/com/pingplusplus/balance/BalanceTransactionTest.java diff --git a/src/test/java/com/pingplusplus/balance/BalanceTransferTest.java b/pingpp-sdk/src/test/java/com/pingplusplus/balance/BalanceTransferTest.java similarity index 100% rename from src/test/java/com/pingplusplus/balance/BalanceTransferTest.java rename to pingpp-sdk/src/test/java/com/pingplusplus/balance/BalanceTransferTest.java diff --git a/src/test/java/com/pingplusplus/coupon/CouponTemplateTest.java b/pingpp-sdk/src/test/java/com/pingplusplus/coupon/CouponTemplateTest.java similarity index 100% rename from src/test/java/com/pingplusplus/coupon/CouponTemplateTest.java rename to pingpp-sdk/src/test/java/com/pingplusplus/coupon/CouponTemplateTest.java diff --git a/src/test/java/com/pingplusplus/coupon/CouponTest.java b/pingpp-sdk/src/test/java/com/pingplusplus/coupon/CouponTest.java similarity index 100% rename from src/test/java/com/pingplusplus/coupon/CouponTest.java rename to pingpp-sdk/src/test/java/com/pingplusplus/coupon/CouponTest.java diff --git a/src/test/java/com/pingplusplus/order/OrderTest.java b/pingpp-sdk/src/test/java/com/pingplusplus/order/OrderTest.java similarity index 100% rename from src/test/java/com/pingplusplus/order/OrderTest.java rename to pingpp-sdk/src/test/java/com/pingplusplus/order/OrderTest.java diff --git a/src/test/java/com/pingplusplus/order/RoyaltySettlementTest.java b/pingpp-sdk/src/test/java/com/pingplusplus/order/RoyaltySettlementTest.java similarity index 100% rename from src/test/java/com/pingplusplus/order/RoyaltySettlementTest.java rename to pingpp-sdk/src/test/java/com/pingplusplus/order/RoyaltySettlementTest.java diff --git a/src/test/java/com/pingplusplus/order/RoyaltyTemplateTest.java b/pingpp-sdk/src/test/java/com/pingplusplus/order/RoyaltyTemplateTest.java similarity index 100% rename from src/test/java/com/pingplusplus/order/RoyaltyTemplateTest.java rename to pingpp-sdk/src/test/java/com/pingplusplus/order/RoyaltyTemplateTest.java diff --git a/src/test/java/com/pingplusplus/order/RoyaltyTest.java b/pingpp-sdk/src/test/java/com/pingplusplus/order/RoyaltyTest.java similarity index 100% rename from src/test/java/com/pingplusplus/order/RoyaltyTest.java rename to pingpp-sdk/src/test/java/com/pingplusplus/order/RoyaltyTest.java diff --git a/src/test/java/com/pingplusplus/order/RoyaltyTransactionTest.java b/pingpp-sdk/src/test/java/com/pingplusplus/order/RoyaltyTransactionTest.java similarity index 100% rename from src/test/java/com/pingplusplus/order/RoyaltyTransactionTest.java rename to pingpp-sdk/src/test/java/com/pingplusplus/order/RoyaltyTransactionTest.java diff --git a/src/test/java/com/pingplusplus/recharge/RechargeTest.java b/pingpp-sdk/src/test/java/com/pingplusplus/recharge/RechargeTest.java similarity index 100% rename from src/test/java/com/pingplusplus/recharge/RechargeTest.java rename to pingpp-sdk/src/test/java/com/pingplusplus/recharge/RechargeTest.java diff --git a/src/test/java/com/pingplusplus/settle_account/SettleAccountTest.java b/pingpp-sdk/src/test/java/com/pingplusplus/settle_account/SettleAccountTest.java similarity index 100% rename from src/test/java/com/pingplusplus/settle_account/SettleAccountTest.java rename to pingpp-sdk/src/test/java/com/pingplusplus/settle_account/SettleAccountTest.java diff --git a/src/test/java/com/pingplusplus/split/ProfitTransactionTest.java b/pingpp-sdk/src/test/java/com/pingplusplus/split/ProfitTransactionTest.java similarity index 100% rename from src/test/java/com/pingplusplus/split/ProfitTransactionTest.java rename to pingpp-sdk/src/test/java/com/pingplusplus/split/ProfitTransactionTest.java diff --git a/src/test/java/com/pingplusplus/split/SplitProfitTest.java b/pingpp-sdk/src/test/java/com/pingplusplus/split/SplitProfitTest.java similarity index 100% rename from src/test/java/com/pingplusplus/split/SplitProfitTest.java rename to pingpp-sdk/src/test/java/com/pingplusplus/split/SplitProfitTest.java diff --git a/src/test/java/com/pingplusplus/split/SplitReceiverTest.java b/pingpp-sdk/src/test/java/com/pingplusplus/split/SplitReceiverTest.java similarity index 100% rename from src/test/java/com/pingplusplus/split/SplitReceiverTest.java rename to pingpp-sdk/src/test/java/com/pingplusplus/split/SplitReceiverTest.java diff --git a/src/test/java/com/pingplusplus/sub_app/SubAppChannelTest.java b/pingpp-sdk/src/test/java/com/pingplusplus/sub_app/SubAppChannelTest.java similarity index 100% rename from src/test/java/com/pingplusplus/sub_app/SubAppChannelTest.java rename to pingpp-sdk/src/test/java/com/pingplusplus/sub_app/SubAppChannelTest.java diff --git a/src/test/java/com/pingplusplus/sub_app/SubAppTest.java b/pingpp-sdk/src/test/java/com/pingplusplus/sub_app/SubAppTest.java similarity index 100% rename from src/test/java/com/pingplusplus/sub_app/SubAppTest.java rename to pingpp-sdk/src/test/java/com/pingplusplus/sub_app/SubAppTest.java diff --git a/src/test/java/com/pingplusplus/transfer/TransferTest.java b/pingpp-sdk/src/test/java/com/pingplusplus/transfer/TransferTest.java similarity index 100% rename from src/test/java/com/pingplusplus/transfer/TransferTest.java rename to pingpp-sdk/src/test/java/com/pingplusplus/transfer/TransferTest.java diff --git a/src/test/java/com/pingplusplus/user/UserTest.java b/pingpp-sdk/src/test/java/com/pingplusplus/user/UserTest.java similarity index 100% rename from src/test/java/com/pingplusplus/user/UserTest.java rename to pingpp-sdk/src/test/java/com/pingplusplus/user/UserTest.java diff --git a/src/test/java/com/pingplusplus/withdrawal/BatchWithdrawalTest.java b/pingpp-sdk/src/test/java/com/pingplusplus/withdrawal/BatchWithdrawalTest.java similarity index 100% rename from src/test/java/com/pingplusplus/withdrawal/BatchWithdrawalTest.java rename to pingpp-sdk/src/test/java/com/pingplusplus/withdrawal/BatchWithdrawalTest.java diff --git a/src/test/java/com/pingplusplus/withdrawal/WithdrawalTest.java b/pingpp-sdk/src/test/java/com/pingplusplus/withdrawal/WithdrawalTest.java similarity index 100% rename from src/test/java/com/pingplusplus/withdrawal/WithdrawalTest.java rename to pingpp-sdk/src/test/java/com/pingplusplus/withdrawal/WithdrawalTest.java diff --git a/pom.xml b/pom.xml index edca653..8d6e957 100644 --- a/pom.xml +++ b/pom.xml @@ -1,23 +1,63 @@ - + 4.0.0 - Pingplusplus - pingpp-java - 2.4.0 + + com.pingxx + pingpp-java-parent + 2.5.7 + pom + + A Java SDK for Ping++ Payment API. + + + Apache License 2.0 + http://www.apache.org/licenses/LICENSE-2.0 + repo + + + + + scm:git:https://github.com/PingPlusPlus/pingpp-java.git + + + + afon + Afon + xufeng.weng@pingxx.com + + + + + pingpp-sdk + + + + 1.8 + 1.8 + + - - com.google.code.gson - gson - 2.8.5 - runtime - - - commons-codec - commons-codec - 1.10 - runtime - - pingpp-java - https://github.com/PingPlusPlus/pingpp-java + + + + + org.apache.maven.plugins + maven-install-plugin + 2.5.2 + + true + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.7 + + true + + + + diff --git a/settings.gradle b/settings.gradle index 05ca5f7..c5a152d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,2 +1,3 @@ rootProject.name = 'pingpp-java' +include 'pingpp-sdk' include 'example' diff --git a/src/main/java/com/pingplusplus/model/Contact.java b/src/main/java/com/pingplusplus/model/Contact.java deleted file mode 100644 index caf2b50..0000000 --- a/src/main/java/com/pingplusplus/model/Contact.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.pingplusplus.model; - -import com.pingplusplus.exception.PingppException; -import com.pingplusplus.net.APIResource; -import com.pingplusplus.net.AppBasedResource; -import com.pingplusplus.net.RequestOptions; - -import java.util.Map; - -public class Contact extends AppBasedResource { - String user; - Boolean livemode; - String accNo; - String contactNo; - - public String getUser() { - return user; - } - - public void setUser(String user) { - this.user = user; - } - - public String getAccNo() { - return accNo; - } - - public void setAccNo(String accNo) { - this.accNo = accNo; - } - - public Boolean getLivemode() { - return livemode; - } - - public void setLivemode(Boolean livemode) { - this.livemode = livemode; - } - - public String getContactNo() { - return contactNo; - } - - public void setContactNo(String contactNo) { - this.contactNo = contactNo; - } - - /** - * 新增联系人 - * - * @param params 请求参数 - * @return Contact - * @throws PingppException - */ - public static Contact create(Map params) - throws PingppException { - return create(params, null); - } - - /** - * 新增联系人 - * - * @param params 请求参数 - * @param options the specific options - * @return Contact - * @throws PingppException - */ - public static Contact create(Map params, RequestOptions options) - throws PingppException { - return APIResource.request(APIResource.RequestMethod.POST, singleClassURL(Contact.class), params, Contact.class, options); - } -} diff --git a/src/main/java/com/pingplusplus/model/SubBank.java b/src/main/java/com/pingplusplus/model/SubBank.java deleted file mode 100644 index 1f93941..0000000 --- a/src/main/java/com/pingplusplus/model/SubBank.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.pingplusplus.model; - -import com.pingplusplus.exception.PingppException; -import com.pingplusplus.net.APIResource; -import com.pingplusplus.net.RequestOptions; - -import java.util.Map; - -public class SubBank extends APIResource { - String object; - String subBank; - String subBankCode; - String prov; - String city; - - public String getObject() { - return object; - } - - public void setObject(String object) { - this.object = object; - } - - public String getSubBank() { - return subBank; - } - - public void setSubBank(String subBank) { - this.subBank = subBank; - } - - public String getSubBankCode() { - return subBankCode; - } - - public void setSubBankCode(String subBankCode) { - this.subBankCode = subBankCode; - } - - public String getProv() { - return prov; - } - - public void setProv(String prov) { - this.prov = prov; - } - - public String getCity() { - return city; - } - - public void setCity(String city) { - this.city = city; - } - - /** - * 银行支行列表查询 - * - * @param params 银行编号和省市信息 - * @return SubBankCollection - * @throws PingppException - */ - public static SubBankCollection query(Map params) - throws PingppException { - return query(params, null); - } - - /** - * 银行支行列表查询 - * - * @param params 银行编号和省市信息 - * @param options the specific options - * @return SubBankCollection - * @throws PingppException - */ - public static SubBankCollection query(Map params, RequestOptions options) - throws PingppException { - return APIResource.request(APIResource.RequestMethod.GET, classURL(SubBank.class), params, SubBankCollection.class, options); - } -} diff --git a/src/main/java/com/pingplusplus/model/SubBankCollection.java b/src/main/java/com/pingplusplus/model/SubBankCollection.java deleted file mode 100644 index dd410ce..0000000 --- a/src/main/java/com/pingplusplus/model/SubBankCollection.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.pingplusplus.model; - -public class SubBankCollection extends PingppCollection { -} diff --git a/src/main/java/com/pingplusplus/model/UserPic.java b/src/main/java/com/pingplusplus/model/UserPic.java deleted file mode 100644 index b1e5ed1..0000000 --- a/src/main/java/com/pingplusplus/model/UserPic.java +++ /dev/null @@ -1,108 +0,0 @@ -package com.pingplusplus.model; - -import com.pingplusplus.exception.PingppException; -import com.pingplusplus.net.APIResource; -import com.pingplusplus.net.AppBasedResource; -import com.pingplusplus.net.RequestOptions; - -import java.util.Map; - -public class UserPic extends AppBasedResource { - String user; - Boolean livemode; - String type; - String operateType; - String picFmt; - String picType; - String accNo; - String picExternalId; - - public String getUser() { - return user; - } - - public void setUser(String user) { - this.user = user; - } - - public Boolean getLivemode() { - return livemode; - } - - public void setLivemode(Boolean livemode) { - this.livemode = livemode; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public String getOperateType() { - return operateType; - } - - public void setOperateType(String operateType) { - this.operateType = operateType; - } - - public String getPicFmt() { - return picFmt; - } - - public void setPicFmt(String picFmt) { - this.picFmt = picFmt; - } - - public String getPicType() { - return picType; - } - - public void setPicType(String picType) { - this.picType = picType; - } - - public String getAccNo() { - return accNo; - } - - public void setAccNo(String accNo) { - this.accNo = accNo; - } - - public String getPicExternalId() { - return picExternalId; - } - - public void setPicExternalId(String picExternalId) { - this.picExternalId = picExternalId; - } - - /** - * 证件上传 - * - * @param params 请求参数 - * @return UserPic - * @throws PingppException - */ - public static UserPic upload(Map params) - throws PingppException { - return upload(params, null); - } - - /** - * 证件上传 - * - * @param params 请求参数 - * @param options the specific options - * @return UserPic - * @throws PingppException - */ - public static UserPic upload(Map params, RequestOptions options) - throws PingppException { - return APIResource.request(APIResource.RequestMethod.POST, singleClassURL(UserPic.class), params, UserPic.class, options); - } -} diff --git a/src/main/java/com/pingplusplus/util/PingppSignature.java b/src/main/java/com/pingplusplus/util/PingppSignature.java deleted file mode 100644 index e234427..0000000 --- a/src/main/java/com/pingplusplus/util/PingppSignature.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.pingplusplus.util; - -import com.pingplusplus.Pingpp; -import org.apache.commons.codec.binary.Base64; - -import java.io.UnsupportedEncodingException; -import java.security.*; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.PKCS8EncodedKeySpec; - -/** - * Created by Afon on 2016/12/16. - */ -public class PingppSignature { - public static String sign(String data, String PEMEncodedPrivateKey, String charset) { - PrivateKey privateKey = getPrivateKeyFromPEM(PEMEncodedPrivateKey); - if (privateKey == null) { - return null; - } - - try { - Signature signature = Signature.getInstance("SHA256withRSA"); - signature.initSign(privateKey); - signature.update(data.getBytes(charset)); - byte[] signBytes = signature.sign(); - - return Base64.encodeBase64String(signBytes).replaceAll("[\n\r]", ""); - } catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException | UnsupportedEncodingException e) { - e.printStackTrace(); - } - - return null; - } - - public static PrivateKey getPrivateKeyFromPEM(String PEMEncodedPrivateKey) { - PEMEncodedPrivateKey = PEMEncodedPrivateKey - .replaceAll("(-+BEGIN (RSA )?PRIVATE KEY-+\\r?\\n|-+END (RSA )?PRIVATE KEY-+\\r?\\n?)", ""); - byte[] privateKeyBytes = Base64.decodeBase64(PEMEncodedPrivateKey); - - try { - return generatePrivateKeyWithPKCS8(privateKeyBytes); - } catch (InvalidKeySpecException e) { - if (Pingpp.DEBUG) { - e.printStackTrace(); - } - return null; - } - } - - public static PrivateKey generatePrivateKeyWithPKCS8(byte[] privateKeyBytes) - throws InvalidKeySpecException { - try { - PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes); - KeyFactory kf = KeyFactory.getInstance("RSA"); - return kf.generatePrivate(keySpec); - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - } - return null; - } -} diff --git a/src/test/java/com/pingplusplus/SubBankTest.java b/src/test/java/com/pingplusplus/SubBankTest.java deleted file mode 100644 index 1094a0e..0000000 --- a/src/test/java/com/pingplusplus/SubBankTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.pingplusplus; - -import com.pingplusplus.exception.PingppException; -import com.pingplusplus.model.SubBank; -import com.pingplusplus.model.SubBankCollection; -import org.junit.Test; - -import java.util.HashMap; -import java.util.Map; - -import static org.junit.Assert.assertEquals; - -public class SubBankTest extends PingppTestBase { - /** - * 银行支行列表查询 - */ - @Test public void testSubBankQuery() throws PingppException { - Map params = new HashMap<>(); - params.put("app", PingppTestData.getAppID()); - params.put("open_bank_code", "0308"); // 银行开户行编号 - params.put("prov", "浙江省"); // 省份 - params.put("city", "宁波市"); // 城市 - params.put("channel", "chanpay"); // 相关 transfer 渠道 - - SubBankCollection obj = SubBank.query(params); - - System.out.println(obj); - - SubBank subBank = obj.getData().get(0); - - assertEquals("list", obj.getObject()); - assertEquals("sub_bank", subBank.getObject()); - assertEquals("浙江省", subBank.getProv()); - assertEquals("宁波市", subBank.getCity()); - } -} diff --git a/src/test/java/com/pingplusplus/sub_app/ContactTest.java b/src/test/java/com/pingplusplus/sub_app/ContactTest.java deleted file mode 100644 index 174a0a4..0000000 --- a/src/test/java/com/pingplusplus/sub_app/ContactTest.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.pingplusplus.sub_app; - -import com.pingplusplus.PingppTestBase; -import com.pingplusplus.exception.PingppException; -import com.pingplusplus.model.Contact; -import org.junit.Test; - -import java.util.HashMap; -import java.util.Map; - -import static org.junit.Assert.assertEquals; - -/* - * @author Afon, @date 19-07-12 - */ -public class ContactTest extends PingppTestBase { - - /** - * 证件上传 - */ - @Test public void testAddContact() throws PingppException { - Map params = new HashMap<>(); - params.put("user", "test_user_001"); // 用户 ID - params.put("acc_no", "2019057929311601000631"); // 壹账通用户编号。此编号可在证件上传接口的返回获得 - params.put("contact_type", 1); // 联系人类型,1:第一紧急联系人、2:第二紧急联系人、3:第三紧急联系人、4、法人、5、财务人员(4、5必传) - params.put("contact_name", "杨真探"); // 联系人名称 - params.put("contact_cert_type", "01"); // 证件类型:01:身份证、02:企业营业执照、03:护照、04:港澳通行证、05:台湾往来通行证、06:临时身份证 - params.put("contact_cert_no", "310181201402149201"); // 联系人证件号 - params.put("contact_cert_valid_from", "2019-01-01"); // 联系人证件起始日,格式:“2018-12-21” - params.put("contact_cert_valid_until", "2028-12-31"); // 联系人证件截止日,格式:“2019-12-21”,长期填写:“9999-12-31” - params.put("contact_cert_mobile", "13500127654"); // 联系人手机号 - params.put("contact_job_type", 1); // 联系人工作类型,1:企业主 2:个体工商户 3:全职上班 4:兼职 5: 学生 6:退休 7:无固定职业 8:其他 -// params.put("contact_relation_ship", 1); // 联系人与本人关系,1:父母 2:配偶 3:子女 4:兄弟 5:姐妹 6:其他亲属 7:朋友 8:同事 - - Contact obj = Contact.create(params); - - assertEquals("user should be same as param", params.get("user"), obj.getUser()); - assertEquals("acc_no should be same as param", params.get("acc_no"), obj.getAccNo()); - } -} diff --git a/src/test/java/com/pingplusplus/user/UserPicTest.java b/src/test/java/com/pingplusplus/user/UserPicTest.java deleted file mode 100644 index 38ba083..0000000 --- a/src/test/java/com/pingplusplus/user/UserPicTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.pingplusplus.user; - -import com.pingplusplus.PingppTestBase; -import com.pingplusplus.exception.PingppException; -import com.pingplusplus.model.UserPic; -import org.junit.Test; - -import java.util.HashMap; -import java.util.Map; - -import static org.junit.Assert.assertEquals; - -/* - * @author Afon, @date 19-07-12 - */ -public class UserPicTest extends PingppTestBase { - - /** - * 证件上传 - */ - @Test public void testUploadPic() throws PingppException { - Map params = new HashMap(); - params.put("user", "test_user_001"); // 用户 ID,首字母必须是英文数字或者 _-@, 必传 - params.put("type", "customer"); // 用户类型,customer: 对私,business: 对公 -// params.put("acc_no", "2019057929311601000631"); // 壹账通用户编号。覆盖的时候使用,新用户不需要该字段 - params.put("operate_type", "00"); // 操作类型,00: 新增,01: 修改,02: 增开户。不传默认为新增 - // 图片内容,base64 编码 - params.put("pic", "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"); - params.put("pic_fmt", "jpg"); // 图片格式。jpg, png - params.put("pic_type", "102"); // 图片类型,101: 个人身份证信息面,102: 个人身份证国徽面,201: 企业证件照片,202: 法人身份证信息面,203: 法人身份证国徽面 - - UserPic obj = UserPic.upload(params); - - assertEquals("user should be same as param", params.get("user"), obj.getUser()); - } -}