Skip to content

Commit 084f72b

Browse files
committed
Merge branch 'agreement' into 'master'
add impl notify of agreement See merge request api-team/pingpp-java!25
2 parents ccd2122 + 01ea9d7 commit 084f72b

182 files changed

Lines changed: 429 additions & 210 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# ChangeLog
22

3+
## 2.5.0
4+
5+
- 新增: 响应签名验签
6+
- 新增: 微信扣款预扣费通知实现
7+
- 更新: `RequestOptions` 新增`Ping++`公钥设置参数
8+
- 更新: webhook验签方法移动至 `PingppSignature` 类中
9+
- 更新: `gson``2.10`;`commons-codec``1.17.0`
10+
311
## 2.4.1
412

513
- 修改: Refund 对象新增`currency` 字段

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2.4.1
1+
2.5.0

build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ sourceCompatibility = 1.8
1515
targetCompatibility = 1.8
1616

1717
dependencies {
18-
implementation 'com.google.code.gson:gson:2.8.9'
19-
implementation 'commons-codec:commons-codec:1.13'
18+
implementation 'com.google.code.gson:gson:2.10'
19+
implementation 'commons-codec:commons-codec:1.17.0'
2020

2121
testImplementation 'junit:junit:4.13.2'
2222
}

example/build.gradle

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
apply plugin: 'java'
22

33
dependencies {
4-
implementation 'com.google.code.gson:gson:2.8.9'
5-
implementation 'commons-codec:commons-codec:1.13'
4+
implementation 'com.google.code.gson:gson:2.10'
5+
implementation 'commons-codec:commons-codec:1.17.0'
66

7-
implementation 'com.pingxx:pingpp-java:2.4.1'
7+
// 方法 1. 本地文件系统依赖
8+
// implementation fileTree(dir: 'lib', includes: ['*jar'])
9+
// 方法 2. 仓库依赖
10+
// implementation 'com.pingxx:pingpp-sdk:1.2.0'
11+
// 方法 3.模块依赖
12+
implementation project(':pingpp-sdk')
813
}

example/res/pingpp_public_key.pem

Lines changed: 0 additions & 9 deletions
This file was deleted.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.pingxx.example;
2+
3+
import com.pingplusplus.exception.PingppException;
4+
import com.pingplusplus.model.AgreementNotify;
5+
6+
import java.util.HashMap;
7+
import java.util.Map;
8+
9+
public class AgreementNotifyExample {
10+
private String appId;
11+
12+
AgreementNotifyExample(String appId) {
13+
this.appId = appId;
14+
}
15+
16+
public static void runDemos(String appId) {
17+
AgreementNotifyExample example = new AgreementNotifyExample(appId);
18+
System.out.println("------- 创建 charge -------");
19+
example.create("agr_123456");
20+
}
21+
22+
public AgreementNotify create(String id) {
23+
AgreementNotify agreementNotify = null;
24+
Map<String, Object> params = new HashMap<String, Object>();
25+
params.put("amount", 100);
26+
try {
27+
agreementNotify = AgreementNotify.create(id, params);
28+
} catch (PingppException e) {
29+
e.printStackTrace();
30+
}
31+
return agreementNotify;
32+
}
33+
}

example/src/main/java/com/pingxx/example/Main.java

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.pingplusplus.Pingpp;
44

55
import java.math.BigInteger;
6+
import java.net.URL;
67
import java.security.SecureRandom;
78

89
/**
@@ -30,6 +31,8 @@ public class Main {
3031
// 你生成的私钥路径
3132
private final static String privateKeyFilePath = "res/your_rsa_private_key_pkcs8.pem";
3233

34+
private final static String verifyKeyFilePath = "pingpp_public_key.pem";
35+
3336
protected static String projectDir;
3437

3538
public static void main(String[] args) throws Exception {
@@ -43,12 +46,15 @@ public static void main(String[] args) throws Exception {
4346
// 设置私钥路径,用于请求签名
4447
//Pingpp.privateKeyPath = projectDir + privateKeyFilePath;
4548

46-
/**
49+
/*
4750
* 或者直接设置私钥内容
48-
Pingpp.privateKey = "... PKCS8私钥内容字符串 ...";
51+
* Pingpp.privateKey = "... PKCS8私钥内容字符串 ...";
4952
*/
5053
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=";
5154

55+
//(可选,推荐设置)设置响应验证公钥,用于验证响应是否真实有效;不设置或设置密钥错误将不验签
56+
Pingpp.setVerifyPublicKeyPath(getResPath(verifyKeyFilePath));
57+
5258
// Charge 示例
5359
ChargeExample.runDemos(appId);
5460

@@ -82,13 +88,21 @@ public static void main(String[] args) throws Exception {
8288
CustomsExample.runDemos(appId);
8389
}
8490

85-
private static SecureRandom random = new SecureRandom();
91+
private static final SecureRandom random = new SecureRandom();
8692

8793
public static String randomString(int length) {
8894
String str = new BigInteger(130, random).toString(32);
8995
return str.substring(0, length);
9096
}
9197

98+
public static String getResPath(String subPath) {
99+
if (subPath == null) {
100+
throw new NullPointerException();
101+
}
102+
URL resource = Thread.currentThread().getContextClassLoader().getResource(subPath);
103+
return resource != null ? resource.getPath() : subPath;
104+
}
105+
92106
public static int currentTimeSeconds() {
93107
return (int) (System.currentTimeMillis() / 1000);
94108
}

example/src/main/java/com/pingxx/example/WebhooksVerifyExample.java

Lines changed: 13 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -7,37 +7,25 @@
77
*/
88
package com.pingxx.example;
99

10-
import java.io.*;
11-
import java.security.InvalidKeyException;
12-
import java.security.KeyFactory;
13-
import java.security.NoSuchAlgorithmException;
14-
import java.security.PublicKey;
15-
import java.security.Signature;
16-
import java.security.SignatureException;
17-
import java.security.spec.X509EncodedKeySpec;
18-
19-
import org.apache.commons.codec.binary.Base64;
10+
import com.pingplusplus.Pingpp;
11+
import com.pingplusplus.net.APIResource;
12+
import com.pingplusplus.util.PingppSignature;
2013

2114
/**
22-
* Created by sunkai on 15/5/19. webhooks 验证签名示例
23-
*
15+
* webhooks 验证签名示例
16+
* <p>
2417
* 该实例演示如何对 Ping++ webhooks 通知进行验证。
2518
* 验证是为了让开发者确认该通知来自 Ping++ ,防止恶意伪造通知。用户如果有别的验证机制,可以不进行验证签名。
26-
*
19+
* <p>
2720
* 验证签名需要 签名、公钥、验证信息,该实例采用文件存储方式进行演示。
2821
* 实际项目中,需要用户从异步通知的 HTTP header 中读取签名,从 HTTP body 中读取验证信息。公钥的存储方式也需要用户自行设定。
29-
*
30-
* 该实例仅供演示如何验证签名,请务必不要直接 copy 到实际项目中使用。
31-
*
22+
* <p>
23+
* 该实例仅供演示如何验证签名,请务必不要直接 copy 到实际项目中使用。
3224
*/
3325
public class WebhooksVerifyExample {
34-
35-
private static String pubKeyPath = "res/pingpp_public_key.pem";
36-
private static String eventPath = "res/webhooks_raw_post_data.json";
37-
private static String signPath = "res/signature.txt";
38-
3926
/**
4027
* 验证 webhooks 签名,仅供参考
28+
*
4129
* @param args
4230
* @throws Exception
4331
*/
@@ -46,78 +34,17 @@ public static void main(String[] args) throws Exception {
4634
}
4735

4836
public static void runDemos() throws Exception {
37+
String verifyPublicKey = Pingpp.verifyPublicKey;
4938
// 该数据请从 request 中获取原始 POST 请求数据, 以下仅作为示例
50-
String webhooksRawPostData = getStringFromFile(eventPath);
39+
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}";
5140
System.out.println("------- POST 原始数据 -------");
5241
System.out.println(webhooksRawPostData);
5342
// 签名数据请从 request 的 header 中获取, key 为 X-Pingplusplus-Signature (请忽略大小写, 建议自己做格式化)
54-
String signature = getStringFromFile(signPath);
43+
String signature = "Ju5ItDM9Hblkm6Pbb/r3G9iM58oCtAUJlJDrOud0E23TUtGBxKYJSMcuTFbPgLGLKy5QHYovJJojjMjM1CMGCp82F+SOY1U2zuwzkAk0lVqhfQk+CWGpcyUzOtLOOblKidyuIb+axZDTIg4kK/JoOR2xMH3+cJD9BN6rtfzEDbHqyZIfv6n3y/LdZNhsXfQq+qoIRuLdHrFexk1USgk7SXFvH1pCOIt8o2+dryp/ixlNj1vGq57eE8sbEzPo72vxLtzPazlgR+67cS1bUxjzObh7YFbSSTJxkHDEIi3ipTpoiI7Kc5ng7EzsWgp3cAl7on5HztfFLfT8+Bjpdvtr6w==";
5544
System.out.println("------- 签名 -------");
5645
System.out.println(signature);
5746

58-
boolean result = verifyData(webhooksRawPostData, signature, getPubKey());
47+
boolean result = PingppSignature.verify(signature, webhooksRawPostData, verifyPublicKey, APIResource.CHARSET.name());
5948
System.out.println("验签结果:" + (result ? "通过" : "失败"));
6049
}
61-
62-
/**
63-
* 读取文件, 部署 web 程序的时候, 签名和验签内容需要从 request 中获得
64-
* @param filePath
65-
* @return
66-
* @throws Exception
67-
*/
68-
public static String getStringFromFile(String filePath) throws Exception {
69-
FileInputStream in = new FileInputStream(Main.projectDir + filePath);
70-
InputStreamReader inReader = new InputStreamReader(in, "UTF-8");
71-
BufferedReader bf = new BufferedReader(inReader);
72-
StringBuilder sb = new StringBuilder();
73-
String line;
74-
do {
75-
line = bf.readLine();
76-
if (line != null) {
77-
if (sb.length() != 0) {
78-
sb.append("\n");
79-
}
80-
sb.append(line);
81-
}
82-
} while (line != null);
83-
84-
return sb.toString();
85-
}
86-
87-
/**
88-
* 获得公钥
89-
* @return
90-
* @throws Exception
91-
*/
92-
public static PublicKey getPubKey() throws Exception {
93-
String pubKeyString = getStringFromFile(pubKeyPath);
94-
pubKeyString = pubKeyString.replaceAll("(-+BEGIN PUBLIC KEY-+\\r?\\n|-+END PUBLIC KEY-+\\r?\\n?)", "");
95-
byte[] keyBytes = Base64.decodeBase64(pubKeyString.getBytes("UTF-8"));
96-
97-
// generate public key
98-
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
99-
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
100-
101-
return keyFactory.generatePublic(spec);
102-
}
103-
104-
/**
105-
* 验证签名
106-
* @param dataString
107-
* @param signatureString
108-
* @param publicKey
109-
* @return
110-
* @throws NoSuchAlgorithmException
111-
* @throws InvalidKeyException
112-
* @throws SignatureException
113-
*/
114-
public static boolean verifyData(String dataString, String signatureString, PublicKey publicKey)
115-
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, UnsupportedEncodingException {
116-
byte[] signatureBytes = Base64.decodeBase64(signatureString);
117-
Signature signature = Signature.getInstance("SHA256withRSA");
118-
signature.initVerify(publicKey);
119-
signature.update(dataString.getBytes("UTF-8"));
120-
return signature.verify(signatureBytes);
121-
}
122-
12350
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
-----BEGIN PUBLIC KEY-----
2+
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoXV1qdwYGq6lac+EIVxx
3+
1/wGcjjy5InO08QBvUGDQMKAWjg/UvnNmnTZJvxUu18UVtwoHIls24EHZ96im3k0
4+
DPn43aSGyR417g9rqUL2N1H/0dCNcT8LY8sZL/sw/IV//IQcZYv/8EO6+7SClw+G
5+
Jpib84QfmDgUPbQC0dhl1R1Q8e8RoE/+70yX6OY22kRr14KjRZiVQ0UMZS5KaPwS
6+
FJ1egyNDCLFoLyhRQMV5vDxMdpIvxZuGW+3aTZkW8v/ISXWmSFpI/dlpxHp0+h6p
7+
sdai2RzXYuyzbeOPyQEZkyGfR6BX2Gv/l80nEFNup7uNmAt/be9IdpSZlxpF4HlI
8+
8wIDAQAB
9+
-----END PUBLIC KEY-----

libs/pingpp-sdk-2.5.0.jar

161 KB
Binary file not shown.

0 commit comments

Comments
 (0)