-
Notifications
You must be signed in to change notification settings - Fork 288
feat: 新增支付、退款、商家转账批次回调通知解析服务 #381
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| package com.wechat.pay.java.service.payments; | ||
|
|
||
| import com.wechat.pay.java.core.RSAAutoCertificateConfig; | ||
| import com.wechat.pay.java.core.exception.MalformedMessageException; | ||
| import com.wechat.pay.java.core.exception.ValidationException; | ||
| import com.wechat.pay.java.core.notification.NotificationConfig; | ||
| import com.wechat.pay.java.core.notification.RequestParam; | ||
| import com.wechat.pay.java.service.payments.model.Transaction; | ||
|
|
||
| /** PaymentNotificationParseService使用示例 */ | ||
| public class PaymentNotificationParseServiceExample { | ||
|
|
||
| /** 商户号 */ | ||
| public static String merchantId = "190000****"; | ||
|
|
||
| /** 商户API私钥路径 */ | ||
| public static String privateKeyPath = "/Users/yourname/your/path/apiclient_key.pem"; | ||
|
|
||
| /** 商户证书序列号 */ | ||
| public static String merchantSerialNumber = "5157F09EFDC096DE15EBE81A47057A72********"; | ||
|
|
||
| /** 商户APIV3密钥 */ | ||
| public static String apiV3Key = "..."; | ||
|
|
||
| public static PaymentNotificationParseService service; | ||
|
|
||
| public static void main(String[] args) { | ||
| // 初始化商户配置 | ||
| NotificationConfig config = | ||
| new RSAAutoCertificateConfig.Builder() | ||
| .merchantId(merchantId) | ||
| // 使用 com.wechat.pay.java.core.util 中的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名 | ||
| .privateKeyFromPath(privateKeyPath) | ||
| .merchantSerialNumber(merchantSerialNumber) | ||
| .apiV3Key(apiV3Key) | ||
| .build(); | ||
|
|
||
| // 初始化服务 | ||
| service = new PaymentNotificationParseService.Builder().config(config).build(); | ||
| // ... 调用接口 | ||
| try { | ||
| RequestParam requestParam = | ||
| new RequestParam.Builder() | ||
| .serialNumber("5157F09EFDC096DE15EBE81A47057A72********") | ||
| .signature("mm/2CMGxo5qDKNk1i7Szn0IiwAUPlfrp********") | ||
| .timestamp("1723789635") | ||
| .nonce("614275b63d789bd3a7a472c63d809552") | ||
| .body("") | ||
| .signType("WECHATPAY2-SHA256-RSA2048") | ||
| .build(); | ||
| Transaction transaction = payment(requestParam); | ||
| } catch (MalformedMessageException e) { // 回调通知参数不正确、解析通知数据失败 | ||
| // 调用e.getMessage()获取信息打印日志或上报监控,更多方法见MalformedMessageException定义 | ||
| } catch (ValidationException e) { // 签名验证失败 | ||
| // 调用e.getMessage()获取信息打印日志或上报监控,更多方法见ValidationException定义 | ||
| } | ||
| } | ||
|
|
||
| /** 支付回调 */ | ||
| public static Transaction payment(RequestParam requestParam) { | ||
| return service.payment(requestParam); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| package com.wechat.pay.java.service.refund; | ||
|
|
||
| import com.wechat.pay.java.core.RSAAutoCertificateConfig; | ||
| import com.wechat.pay.java.core.exception.MalformedMessageException; | ||
| import com.wechat.pay.java.core.exception.ValidationException; | ||
| import com.wechat.pay.java.core.notification.NotificationConfig; | ||
| import com.wechat.pay.java.core.notification.RequestParam; | ||
| import com.wechat.pay.java.service.refund.model.RefundNotification; | ||
|
|
||
| /** RefundNotificationParseService使用示例 */ | ||
| public class RefundNotificationParseServiceExample { | ||
|
|
||
| /** 商户号 */ | ||
| public static String merchantId = "190000****"; | ||
|
|
||
| /** 商户API私钥路径 */ | ||
| public static String privateKeyPath = "/Users/yourname/your/path/apiclient_key.pem"; | ||
|
|
||
| /** 商户证书序列号 */ | ||
| public static String merchantSerialNumber = "5157F09EFDC096DE15EBE81A47057A72********"; | ||
|
|
||
| /** 商户APIV3密钥 */ | ||
| public static String apiV3Key = "..."; | ||
|
|
||
| public static RefundNotificationParseService service; | ||
|
|
||
| public static void main(String[] args) { | ||
| // 初始化商户配置 | ||
| NotificationConfig config = | ||
| new RSAAutoCertificateConfig.Builder() | ||
| .merchantId(merchantId) | ||
| // 使用 com.wechat.pay.java.core.util 中的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名 | ||
| .privateKeyFromPath(privateKeyPath) | ||
| .merchantSerialNumber(merchantSerialNumber) | ||
| .apiV3Key(apiV3Key) | ||
| .build(); | ||
|
|
||
| // 初始化服务 | ||
| service = new RefundNotificationParseService.Builder().config(config).build(); | ||
| // ... 调用接口 | ||
| try { | ||
| RequestParam requestParam = | ||
| new RequestParam.Builder() | ||
| .serialNumber("5157F09EFDC096DE15EBE81A47057A72********") | ||
| .signature("mm/2CMGxo5qDKNk1i7Szn0IiwAUPlfrp********") | ||
| .timestamp("1723789635") | ||
| .nonce("614275b63d789bd3a7a472c63d809552") | ||
| .body("") | ||
| .signType("WECHATPAY2-SHA256-RSA2048") | ||
| .build(); | ||
| RefundNotification refundNotification = refund(requestParam); | ||
| } catch (MalformedMessageException e) { // 回调通知参数不正确、解析通知数据失败 | ||
| // 调用e.getMessage()获取信息打印日志或上报监控,更多方法见MalformedMessageException定义 | ||
| } catch (ValidationException e) { // 签名验证失败 | ||
| // 调用e.getMessage()获取信息打印日志或上报监控,更多方法见ValidationException定义 | ||
| } | ||
| } | ||
|
|
||
| /** 退款回调 */ | ||
| public static RefundNotification refund(RequestParam requestParam) { | ||
| return service.refund(requestParam); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| package com.wechat.pay.java.service.transferbatch; | ||
|
|
||
| import com.wechat.pay.java.core.RSAAutoCertificateConfig; | ||
| import com.wechat.pay.java.core.exception.MalformedMessageException; | ||
| import com.wechat.pay.java.core.exception.ValidationException; | ||
| import com.wechat.pay.java.core.notification.NotificationConfig; | ||
| import com.wechat.pay.java.core.notification.RequestParam; | ||
| import com.wechat.pay.java.service.transferbatch.model.TransferBatchNotification; | ||
|
|
||
| /** TransferBatchNotificationParseService使用示例 */ | ||
| public class TransferBatchNotificationParseServiceExample { | ||
|
|
||
| /** 商户号 */ | ||
| public static String merchantId = "190000****"; | ||
|
|
||
| /** 商户API私钥路径 */ | ||
| public static String privateKeyPath = "/Users/yourname/your/path/apiclient_key.pem"; | ||
|
|
||
| /** 商户证书序列号 */ | ||
| public static String merchantSerialNumber = "5157F09EFDC096DE15EBE81A47057A72********"; | ||
|
|
||
| /** 商户APIV3密钥 */ | ||
| public static String apiV3Key = "..."; | ||
|
|
||
| public static TransferBatchNotificationParseService service; | ||
|
|
||
| public static void main(String[] args) { | ||
| // 初始化商户配置 | ||
| NotificationConfig config = | ||
| new RSAAutoCertificateConfig.Builder() | ||
| .merchantId(merchantId) | ||
| // 使用 com.wechat.pay.java.core.util 中的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名 | ||
| .privateKeyFromPath(privateKeyPath) | ||
| .merchantSerialNumber(merchantSerialNumber) | ||
| .apiV3Key(apiV3Key) | ||
| .build(); | ||
|
|
||
| // 初始化服务 | ||
| service = new TransferBatchNotificationParseService.Builder().config(config).build(); | ||
| // ... 调用接口 | ||
| try { | ||
| RequestParam requestParam = | ||
| new RequestParam.Builder() | ||
| .serialNumber("5157F09EFDC096DE15EBE81A47057A72********") | ||
| .signature("mm/2CMGxo5qDKNk1i7Szn0IiwAUPlfrp********") | ||
| .timestamp("1723789635") | ||
| .nonce("614275b63d789bd3a7a472c63d809552") | ||
| .body("") | ||
| .signType("WECHATPAY2-SHA256-RSA2048") | ||
| .build(); | ||
| TransferBatchNotification transferBatchNotification = transferBatch(requestParam); | ||
| } catch (MalformedMessageException e) { // 回调通知参数不正确、解析通知数据失败 | ||
| // 调用e.getMessage()获取信息打印日志或上报监控,更多方法见MalformedMessageException定义 | ||
| } catch (ValidationException e) { // 签名验证失败 | ||
| // 调用e.getMessage()获取信息打印日志或上报监控,更多方法见ValidationException定义 | ||
| } | ||
| } | ||
|
|
||
| /** 商家转账批次回调 */ | ||
| public static TransferBatchNotification transferBatch(RequestParam requestParam) { | ||
| return service.transferBatch(requestParam); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| package com.wechat.pay.java.service.payments; | ||
|
|
||
| import static java.util.Objects.requireNonNull; | ||
|
|
||
| import com.wechat.pay.java.core.exception.MalformedMessageException; | ||
| import com.wechat.pay.java.core.exception.ValidationException; | ||
| import com.wechat.pay.java.core.notification.NotificationConfig; | ||
| import com.wechat.pay.java.core.notification.NotificationParser; | ||
| import com.wechat.pay.java.core.notification.RequestParam; | ||
| import com.wechat.pay.java.service.payments.model.Transaction; | ||
|
|
||
| /** | ||
| * 支付回调通知解析服务 | ||
| * | ||
| * <p>它用于对 支付回调 进行验签并解析回调报文。 | ||
| */ | ||
| public class PaymentNotificationParseService { | ||
| private final NotificationParser parser; | ||
|
|
||
| private PaymentNotificationParseService(NotificationParser parser) { | ||
| this.parser = requireNonNull(parser); | ||
| } | ||
|
|
||
| /** PaymentNotificationParseService构造器 */ | ||
| public static class Builder { | ||
|
|
||
| private NotificationConfig config; | ||
| private NotificationParser parser; | ||
|
|
||
| public PaymentNotificationParseService.Builder config(NotificationConfig config) { | ||
| this.config = config; | ||
| return this; | ||
| } | ||
|
|
||
| public PaymentNotificationParseService.Builder parser(NotificationParser parser) { | ||
| this.parser = parser; | ||
| return this; | ||
| } | ||
|
|
||
| public PaymentNotificationParseService build() { | ||
| if (parser == null && config != null) { | ||
| return new PaymentNotificationParseService(new NotificationParser(config)); | ||
| } | ||
| return new PaymentNotificationParseService(parser); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * 支付回调通知解析 | ||
| * | ||
| * @param requestParam 解析通知所需要的请求参数 | ||
| * @return 解析后的回调报文 | ||
| * @throws MalformedMessageException 回调通知参数不正确、解析通知数据失败 | ||
| * @throws ValidationException 签名验证失败 | ||
| */ | ||
| public Transaction payment(RequestParam requestParam) { | ||
| return parser.parse(requestParam, Transaction.class); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,59 @@ | ||||||||||||||||||||||||||||||||||
| package com.wechat.pay.java.service.refund; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| import static java.util.Objects.requireNonNull; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| import com.wechat.pay.java.core.exception.MalformedMessageException; | ||||||||||||||||||||||||||||||||||
| import com.wechat.pay.java.core.exception.ValidationException; | ||||||||||||||||||||||||||||||||||
| import com.wechat.pay.java.core.notification.NotificationConfig; | ||||||||||||||||||||||||||||||||||
| import com.wechat.pay.java.core.notification.NotificationParser; | ||||||||||||||||||||||||||||||||||
| import com.wechat.pay.java.core.notification.RequestParam; | ||||||||||||||||||||||||||||||||||
| import com.wechat.pay.java.service.refund.model.RefundNotification; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||
| * 退款回调通知解析服务 | ||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||
| * <p>它用于对 退款回调 进行验签并解析回调报文。 | ||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||
| public class RefundNotificationParseService { | ||||||||||||||||||||||||||||||||||
| private final NotificationParser parser; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| private RefundNotificationParseService(NotificationParser parser) { | ||||||||||||||||||||||||||||||||||
| this.parser = requireNonNull(parser); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| /** RefundNotificationParseService构造器 */ | ||||||||||||||||||||||||||||||||||
| public static class Builder { | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| private NotificationConfig config; | ||||||||||||||||||||||||||||||||||
| private NotificationParser parser; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| public RefundNotificationParseService.Builder config(NotificationConfig config) { | ||||||||||||||||||||||||||||||||||
| this.config = config; | ||||||||||||||||||||||||||||||||||
| return this; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| public RefundNotificationParseService.Builder parser(NotificationParser parser) { | ||||||||||||||||||||||||||||||||||
| this.parser = parser; | ||||||||||||||||||||||||||||||||||
| return this; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| public RefundNotificationParseService build() { | ||||||||||||||||||||||||||||||||||
| if (parser == null && config != null) { | ||||||||||||||||||||||||||||||||||
| return new RefundNotificationParseService(new NotificationParser(config)); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| return new RefundNotificationParseService(parser); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+40
to
+45
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Caution 🚨 Builder 在未提供 config/parser 时会导致构造时 NPE 与 PaymentNotificationParseService 相同:build() 在 parser==null 且 config==null 时会把 null 传入构造函数并在 requireNonNull 处 NPE。需要显式参数校验。 建议: 同 PaymentNotificationParseService:优先使用显式 parser,否则用 config 构造 parser,否则抛出 IllegalStateException。
Suggested change
|
||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||
| * 退款回调通知解析 | ||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||
| * @param requestParam 解析通知所需要的请求参数 | ||||||||||||||||||||||||||||||||||
| * @return 解析后的回调报文 | ||||||||||||||||||||||||||||||||||
| * @throws MalformedMessageException 回调通知参数不正确、解析通知数据失败 | ||||||||||||||||||||||||||||||||||
| * @throws ValidationException 签名验证失败 | ||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||
| public RefundNotification refund(RequestParam requestParam) { | ||||||||||||||||||||||||||||||||||
| return parser.parse(requestParam, RefundNotification.class); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,59 @@ | ||||||||||||||||||||||||||||||||||
| package com.wechat.pay.java.service.transferbatch; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| import static java.util.Objects.requireNonNull; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| import com.wechat.pay.java.core.exception.MalformedMessageException; | ||||||||||||||||||||||||||||||||||
| import com.wechat.pay.java.core.exception.ValidationException; | ||||||||||||||||||||||||||||||||||
| import com.wechat.pay.java.core.notification.NotificationConfig; | ||||||||||||||||||||||||||||||||||
| import com.wechat.pay.java.core.notification.NotificationParser; | ||||||||||||||||||||||||||||||||||
| import com.wechat.pay.java.core.notification.RequestParam; | ||||||||||||||||||||||||||||||||||
| import com.wechat.pay.java.service.transferbatch.model.TransferBatchNotification; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||
| * 商家转账批次回调通知解析服务 | ||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||
| * <p>它用于对 商家转账批次回调 进行验签并解析回调报文。 | ||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||
| public class TransferBatchNotificationParseService { | ||||||||||||||||||||||||||||||||||
| private final NotificationParser parser; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| private TransferBatchNotificationParseService(NotificationParser parser) { | ||||||||||||||||||||||||||||||||||
| this.parser = requireNonNull(parser); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| /** TransferBatchNotificationParseService构造器 */ | ||||||||||||||||||||||||||||||||||
| public static class Builder { | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| private NotificationConfig config; | ||||||||||||||||||||||||||||||||||
| private NotificationParser parser; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| public TransferBatchNotificationParseService.Builder config(NotificationConfig config) { | ||||||||||||||||||||||||||||||||||
| this.config = config; | ||||||||||||||||||||||||||||||||||
| return this; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| public TransferBatchNotificationParseService.Builder parser(NotificationParser parser) { | ||||||||||||||||||||||||||||||||||
| this.parser = parser; | ||||||||||||||||||||||||||||||||||
| return this; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| public TransferBatchNotificationParseService build() { | ||||||||||||||||||||||||||||||||||
| if (parser == null && config != null) { | ||||||||||||||||||||||||||||||||||
| return new TransferBatchNotificationParseService(new NotificationParser(config)); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| return new TransferBatchNotificationParseService(parser); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+40
to
+45
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Caution 🚨 Builder 在未提供 config/parser 时会导致构造时 NPE 与另外两个 ParseService 相同:build() 未对 (parser==null && config==null) 做保护,会在 requireNonNull(parser) 处抛 NPE。 建议: 同上,增加显式校验并抛出清晰异常信息。
Suggested change
|
||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||
| * 商家转账批次回调通知解析 | ||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||
| * @param requestParam 解析通知所需要的请求参数 | ||||||||||||||||||||||||||||||||||
| * @return 解析后的回调报文 | ||||||||||||||||||||||||||||||||||
| * @throws MalformedMessageException 回调通知参数不正确、解析通知数据失败 | ||||||||||||||||||||||||||||||||||
| * @throws ValidationException 签名验证失败 | ||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||
| public TransferBatchNotification transferBatch(RequestParam requestParam) { | ||||||||||||||||||||||||||||||||||
| return parser.parse(requestParam, TransferBatchNotification.class); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Caution
🚨 Builder 在未提供 config/parser 时会导致构造时 NPE
build() 在 parser==null 且 config==null 时,会执行
new PaymentNotificationParseService(parser),随后构造函数requireNonNull(parser)直接抛出 NullPointerException。该异常不在方法签名/文档中,且对使用者不友好。应在 build() 中对入参组合进行显式校验并抛出更明确的 IllegalArgumentException/IllegalStateException,或提供默认构造路径。建议: 在 build() 中增加对 (parser==null && config==null) 的校验并抛出 IllegalArgumentException/IllegalStateException;同时保持现有优先级:显式 parser 优先,否则用 config 创建 parser。